From 8c74f5ddfd08afbe256d9156ec4a623e4f717209 Mon Sep 17 00:00:00 2001 From: Eva Ho Date: Tue, 4 Nov 2025 15:21:59 -0500 Subject: [PATCH 01/32] ui: using streamdown AI elements for markdown rendering --- app/ui/app/package-lock.json | 1512 ++++++++++++++++- app/ui/app/package.json | 1 + .../StreamingMarkdownContent.stories.tsx | 614 ------- .../StreamingMarkdownContent.test.tsx | 522 ------ .../components/StreamingMarkdownContent.tsx | 415 ++--- app/ui/app/src/index.css | 787 --------- .../app/src/utils/processStreamingMarkdown.ts | 24 - .../app/src/utils/remarkStreamingMarkdown.ts | 447 ----- 8 files changed, 1693 insertions(+), 2629 deletions(-) delete mode 100644 app/ui/app/src/components/StreamingMarkdownContent.stories.tsx delete mode 100644 app/ui/app/src/components/StreamingMarkdownContent.test.tsx delete mode 100644 app/ui/app/src/utils/processStreamingMarkdown.ts delete mode 100644 app/ui/app/src/utils/remarkStreamingMarkdown.ts diff --git a/app/ui/app/package-lock.json b/app/ui/app/package-lock.json index 7877eeae..0dcd91a2 100644 --- a/app/ui/app/package-lock.json +++ b/app/ui/app/package-lock.json @@ -25,6 +25,7 @@ "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-math": "^6.0.0", + "streamdown": "^1.4.0", "unist-builder": "^4.0.0", "unist-util-parents": "^3.0.0" }, @@ -88,6 +89,37 @@ "node": ">=6.0.0" } }, + "node_modules/@antfu/install-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", + "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", + "license": "MIT", + "dependencies": { + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@antfu/install-pkg/node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@antfu/utils": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-9.3.0.tgz", + "integrity": "sha512-9hFT4RauhcUzqOE4f1+frMKLZrgNog5b06I7VmZQV1BkvwvqrbC8EBZf3L1eEL2AKb6rNKjER0sEvJiSP1FXEA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -422,6 +454,51 @@ "node": ">=18" } }, + "node_modules/@braintree/sanitize-url": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.1.tgz", + "integrity": "sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==", + "license": "MIT" + }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz", + "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/gast": "11.0.3", + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/gast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz", + "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/regexp-to-ast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", + "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==", + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/types": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz", + "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==", + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/utils": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz", + "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==", + "license": "Apache-2.0" + }, "node_modules/@chromatic-com/storybook": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@chromatic-com/storybook/-/storybook-4.0.1.tgz", @@ -2353,6 +2430,40 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "license": "MIT" + }, + "node_modules/@iconify/utils": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.0.2.tgz", + "integrity": "sha512-EfJS0rLfVuRuJRn4psJHtK2A9TqVnkxPpHY6lYHiB9+8eSuudsxbwMiavocG45ujOo6FJ+CIRlRnlOGinzkaGQ==", + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^1.1.0", + "@antfu/utils": "^9.2.0", + "@iconify/types": "^2.0.0", + "debug": "^4.4.1", + "globals": "^15.15.0", + "kolorist": "^1.8.0", + "local-pkg": "^1.1.1", + "mlly": "^1.7.4" + } + }, + "node_modules/@iconify/utils/node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -2486,6 +2597,15 @@ "react": ">=16" } }, + "node_modules/@mermaid-js/parser": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.3.tgz", + "integrity": "sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA==", + "license": "MIT", + "dependencies": { + "langium": "3.3.1" + } + }, "node_modules/@neoconfetti/react": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@neoconfetti/react/-/react-1.0.0.tgz", @@ -2962,6 +3082,73 @@ "win32" ] }, + "node_modules/@shikijs/core": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.14.0.tgz", + "integrity": "sha512-qRSeuP5vlYHCNUIrpEBQFO7vSkR7jn7Kv+5X3FO/zBKVDGQbcnlScD3XhkrHi/R8Ltz0kEjvFR9Szp/XMRbFMw==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.14.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.5" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.14.0.tgz", + "integrity": "sha512-3v1kAXI2TsWQuwv86cREH/+FK9Pjw3dorVEykzQDhwrZj0lwsHYlfyARaKmn6vr5Gasf8aeVpb8JkzeWspxOLQ==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.14.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.3" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.14.0.tgz", + "integrity": "sha512-TNcYTYMbJyy+ZjzWtt0bG5y4YyMIWC2nyePz+CFMWqm+HnZZyy9SWMgo8Z6KBJVIZnx8XUXS8U2afO6Y0g1Oug==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.14.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@shikijs/langs": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.14.0.tgz", + "integrity": "sha512-DIB2EQY7yPX1/ZH7lMcwrK5pl+ZkP/xoSpUzg9YC8R+evRCCiSQ7yyrvEyBsMnfZq4eBzLzBlugMyTAf13+pzg==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.14.0" + } + }, + "node_modules/@shikijs/themes": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.14.0.tgz", + "integrity": "sha512-fAo/OnfWckNmv4uBoUu6dSlkcBc+SA1xzj5oUSaz5z3KqHtEbUypg/9xxgJARtM6+7RVm0Q6Xnty41xA1ma1IA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.14.0" + } + }, + "node_modules/@shikijs/types": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.14.0.tgz", + "integrity": "sha512-bQGgC6vrY8U/9ObG1Z/vTro+uclbjjD/uG58RvfxKZVD5p9Yc1ka3tVyEFy7BNJLzxuWyHH5NWynP9zZZS59eQ==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "license": "MIT" + }, "node_modules/@storybook/addon-a11y": { "version": "9.0.14", "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-9.0.14.tgz", @@ -4030,6 +4217,259 @@ "@types/deep-eql": "*" } }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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://registry.npmjs.org/@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/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -4057,19 +4497,23 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, "license": "MIT" }, "node_modules/@types/estree-jsx": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", - "dev": true, "license": "MIT", "dependencies": { "@types/estree": "*" } }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, "node_modules/@types/hast": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", @@ -4134,7 +4578,6 @@ "version": "19.1.7", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.7.tgz", "integrity": "sha512-BnsPLV43ddr05N71gaGzyZ5hzkCmGwhMvYc8zmvI8Ci1bRkkDSzDDVfAXfN2tk748OwI7ediiPX6PfT9p0QGVg==", - "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.0.2" @@ -4157,6 +4600,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -4681,7 +5131,6 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -5042,7 +5491,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "dev": true, "license": "MIT", "funding": { "type": "github", @@ -5097,7 +5545,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "dev": true, "license": "MIT", "funding": { "type": "github", @@ -5134,6 +5581,32 @@ "node": ">= 16" } }, + "node_modules/chevrotain": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", + "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/cst-dts-gen": "11.0.3", + "@chevrotain/gast": "11.0.3", + "@chevrotain/regexp-to-ast": "11.0.3", + "@chevrotain/types": "11.0.3", + "@chevrotain/utils": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/chevrotain-allstar": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", + "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", + "license": "MIT", + "dependencies": { + "lodash-es": "^4.17.21" + }, + "peerDependencies": { + "chevrotain": "^11.0.0" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -5260,6 +5733,12 @@ "dev": true, "license": "MIT" }, + "node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "license": "MIT" + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -5267,6 +5746,15 @@ "dev": true, "license": "MIT" }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "license": "MIT", + "dependencies": { + "layout-base": "^1.0.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -5453,6 +5941,520 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, + "node_modules/cytoscape": { + "version": "3.33.1", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", + "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "license": "MIT", + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "license": "MIT", + "dependencies": { + "cose-base": "^2.2.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "license": "MIT", + "dependencies": { + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==", + "license": "MIT" + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/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-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "license": "BSD-3-Clause", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "license": "ISC" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/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://registry.npmjs.org/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://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "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/dagre-d3-es": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.13.tgz", + "integrity": "sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q==", + "license": "MIT", + "dependencies": { + "d3": "^7.9.0", + "lodash-es": "^4.17.21" + } + }, + "node_modules/dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", @@ -5510,6 +6512,15 @@ "node": ">=8" } }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -5572,6 +6583,15 @@ "dev": true, "license": "MIT" }, + "node_modules/dompurify": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.0.tgz", + "integrity": "sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -5906,7 +6926,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", - "dev": true, "license": "MIT", "funding": { "type": "opencollective", @@ -5940,6 +6959,12 @@ "node": ">=12.0.0" } }, + "node_modules/exsolve": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", + "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "license": "MIT" + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -6296,6 +7321,12 @@ "dev": true, "license": "MIT" }, + "node_modules/hachure-fill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", + "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==", + "license": "MIT" + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -6454,11 +7485,33 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-to-jsx-runtime": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", - "dev": true, "license": "MIT", "dependencies": { "@types/estree": "^1.0.0", @@ -6544,7 +7597,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "dev": true, "license": "MIT", "dependencies": { "@types/hast": "^3.0.0" @@ -6582,7 +7634,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", - "dev": true, "license": "MIT", "funding": { "type": "opencollective", @@ -6599,6 +7650,18 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -6650,9 +7713,17 @@ "version": "0.2.4", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", - "dev": true, "license": "MIT" }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/is-alphabetical": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", @@ -7002,6 +8073,11 @@ "json-buffer": "3.0.1" } }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -7012,6 +8088,34 @@ "node": ">=6" } }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", + "license": "MIT" + }, + "node_modules/langium": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/langium/-/langium-3.3.1.tgz", + "integrity": "sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==", + "license": "MIT", + "dependencies": { + "chevrotain": "~11.0.3", + "chevrotain-allstar": "~0.3.0", + "vscode-languageserver": "~9.0.1", + "vscode-languageserver-textdocument": "~1.0.11", + "vscode-uri": "~3.0.8" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", + "license": "MIT" + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -7265,6 +8369,23 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/local-pkg": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz", + "integrity": "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==", + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.3.0", + "quansync": "^0.2.11" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -7288,6 +8409,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, "node_modules/lodash.castarray": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", @@ -7336,6 +8463,15 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "0.542.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.542.0.tgz", + "integrity": "sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/lz-string": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", @@ -7401,18 +8537,28 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", - "dev": true, "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/marked": { + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.1.tgz", + "integrity": "sha512-ntROs7RaN3EvWfy3EZi14H4YxmT6A5YvywfhO+0pm+cH/dnSQRmdAmoFIc3B9aiwTehyk7pESH4ofyBY+V5hZg==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/mdast-util-find-and-replace": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", - "dev": true, "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -7429,7 +8575,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -7466,7 +8611,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", - "dev": true, "license": "MIT", "dependencies": { "mdast-util-from-markdown": "^2.0.0", @@ -7486,7 +8630,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", - "dev": true, "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -7504,7 +8647,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", - "dev": true, "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -7520,7 +8662,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "dev": true, "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -7538,7 +8679,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "dev": true, "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -7555,7 +8695,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.0.tgz", "integrity": "sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==", - "dev": true, "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -7592,7 +8731,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", - "dev": true, "license": "MIT", "dependencies": { "@types/estree-jsx": "^1.0.0", @@ -7611,7 +8749,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", - "dev": true, "license": "MIT", "dependencies": { "@types/estree-jsx": "^1.0.0", @@ -7636,7 +8773,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", - "dev": true, "license": "MIT", "dependencies": { "@types/estree-jsx": "^1.0.0", @@ -7730,6 +8866,34 @@ "node": ">= 8" } }, + "node_modules/mermaid": { + "version": "11.12.1", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.12.1.tgz", + "integrity": "sha512-UlIZrRariB11TY1RtTgUWp65tphtBv4CSq7vyS2ZZ2TgoMjs2nloq+wFqxiwcxlhHUvs7DPGgMjs2aeQxz5h9g==", + "license": "MIT", + "dependencies": { + "@braintree/sanitize-url": "^7.1.1", + "@iconify/utils": "^3.0.1", + "@mermaid-js/parser": "^0.6.3", + "@types/d3": "^7.4.3", + "cytoscape": "^3.29.3", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", + "d3": "^7.9.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.13", + "dayjs": "^1.11.18", + "dompurify": "^3.2.5", + "katex": "^0.16.22", + "khroma": "^2.1.0", + "lodash-es": "^4.17.21", + "marked": "^16.2.1", + "roughjs": "^4.6.6", + "stylis": "^4.3.6", + "ts-dedent": "^2.2.0", + "uuid": "^11.1.0" + } + }, "node_modules/micromark": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", @@ -7803,7 +8967,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "dev": true, "license": "MIT", "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", @@ -7824,7 +8987,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", - "dev": true, "license": "MIT", "dependencies": { "micromark-util-character": "^2.0.0", @@ -7841,7 +9003,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", - "dev": true, "license": "MIT", "dependencies": { "devlop": "^1.0.0", @@ -7862,7 +9023,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", - "dev": true, "license": "MIT", "dependencies": { "devlop": "^1.0.0", @@ -7881,7 +9041,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", - "dev": true, "license": "MIT", "dependencies": { "devlop": "^1.0.0", @@ -7899,7 +9058,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", - "dev": true, "license": "MIT", "dependencies": { "micromark-util-types": "^2.0.0" @@ -7913,7 +9071,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", - "dev": true, "license": "MIT", "dependencies": { "devlop": "^1.0.0", @@ -8424,6 +9581,35 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/mlly": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, + "node_modules/mlly/node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "license": "MIT" + }, + "node_modules/mlly/node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, "node_modules/motion-dom": { "version": "12.17.0", "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.17.0.tgz", @@ -8515,6 +9701,23 @@ "whatwg-fetch": "^3.6.20" } }, + "node_modules/oniguruma-parser": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz", + "integrity": "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==", + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.3.tgz", + "integrity": "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==", + "license": "MIT", + "dependencies": { + "oniguruma-parser": "^0.12.1", + "regex": "^6.0.1", + "regex-recursion": "^6.0.2" + } + }, "node_modules/open": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", @@ -8590,6 +9793,12 @@ "dev": true, "license": "BlueOak-1.0.0" }, + "node_modules/package-manager-detector": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.5.0.tgz", + "integrity": "sha512-uBj69dVlYe/+wxj8JOpr97XfsxH/eumMt6HqjNTmJDf/6NO9s+0uxeOneIz3AsPt2m6y9PqzDzd3ATcU17MNfw==", + "license": "MIT" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -8646,6 +9855,12 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/path-data-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==", + "license": "MIT" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -8701,7 +9916,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, "license": "MIT" }, "node_modules/pathval": { @@ -8734,6 +9948,17 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, "node_modules/playwright": { "version": "1.53.2", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.2.tgz", @@ -8781,6 +10006,22 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", + "license": "MIT" + }, + "node_modules/points-on-path": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "license": "MIT", + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, "node_modules/postcss": { "version": "8.5.4", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz", @@ -9777,6 +11018,22 @@ "node": ">=6" } }, + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -9862,7 +11119,6 @@ "version": "10.1.0", "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", - "dev": true, "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -10024,6 +11280,36 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.0.1.tgz", + "integrity": "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "license": "MIT" + }, + "node_modules/rehype-harden": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/rehype-harden/-/rehype-harden-1.1.5.tgz", + "integrity": "sha512-JrtBj5BVd/5vf3H3/blyJatXJbzQfRT9pJBmjafbTaPouQCAKxHwRyCc7dle9BXQKxv4z1OzZylz/tNamoiG3A==", + "license": "MIT" + }, "node_modules/rehype-katex": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-7.0.1.tgz", @@ -10122,7 +11408,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", - "dev": true, "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -10157,7 +11442,6 @@ "version": "11.0.0", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "dev": true, "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -10174,7 +11458,6 @@ "version": "11.1.2", "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", - "dev": true, "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -10192,7 +11475,6 @@ "version": "11.0.0", "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "dev": true, "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -10256,6 +11538,12 @@ "node": ">=0.10.0" } }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "license": "Unlicense" + }, "node_modules/rollup": { "version": "4.42.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.42.0.tgz", @@ -10303,6 +11591,18 @@ "dev": true, "license": "MIT" }, + "node_modules/roughjs": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", + "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", + "license": "MIT", + "dependencies": { + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -10327,6 +11627,18 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/scheduler": { "version": "0.26.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", @@ -10387,6 +11699,22 @@ "node": ">=8" } }, + "node_modules/shiki": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.14.0.tgz", + "integrity": "sha512-J0yvpLI7LSig3Z3acIuDLouV5UCKQqu8qOArwMx+/yPVC3WRMgrP67beaG8F+j4xfEWE0eVC4GeBCIXeOPra1g==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "3.14.0", + "@shikijs/engine-javascript": "3.14.0", + "@shikijs/engine-oniguruma": "3.14.0", + "@shikijs/langs": "3.14.0", + "@shikijs/themes": "3.14.0", + "@shikijs/types": "3.14.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", @@ -10532,6 +11860,30 @@ "node": ">=10" } }, + "node_modules/streamdown": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/streamdown/-/streamdown-1.4.0.tgz", + "integrity": "sha512-ylhDSQ4HpK5/nAH9v7OgIIdGJxlJB2HoYrYkJNGrO8lMpnWuKUcrz/A8xAMwA6eILA27469vIavcOTjmxctrKg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1", + "katex": "^0.16.22", + "lucide-react": "^0.542.0", + "marked": "^16.2.1", + "mermaid": "^11.11.0", + "react-markdown": "^10.1.0", + "rehype-harden": "^1.1.5", + "rehype-katex": "^7.0.1", + "rehype-raw": "^7.0.0", + "remark-gfm": "^4.0.1", + "remark-math": "^6.0.0", + "shiki": "^3.12.2", + "tailwind-merge": "^3.3.1" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -10590,7 +11942,6 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "dev": true, "license": "MIT", "dependencies": { "character-entities-html4": "^2.0.0", @@ -10707,7 +12058,6 @@ "version": "1.1.16", "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.16.tgz", "integrity": "sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw==", - "dev": true, "license": "MIT", "dependencies": { "style-to-object": "1.0.8" @@ -10717,12 +12067,17 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz", "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==", - "dev": true, "license": "MIT", "dependencies": { "inline-style-parser": "0.2.4" } }, + "node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "license": "MIT" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -10754,6 +12109,16 @@ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" }, + "node_modules/tailwind-merge": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz", + "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", @@ -11001,7 +12366,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.10" @@ -11118,6 +12482,12 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/ufo": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "license": "MIT" + }, "node_modules/undici-types": { "version": "7.14.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", @@ -11385,6 +12755,19 @@ "dev": true, "license": "MIT" }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, "node_modules/vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", @@ -11659,6 +13042,55 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "license": "MIT" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "license": "MIT" + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "license": "MIT" + }, "node_modules/web-namespaces": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", diff --git a/app/ui/app/package.json b/app/ui/app/package.json index 052e0e62..5532e70f 100644 --- a/app/ui/app/package.json +++ b/app/ui/app/package.json @@ -34,6 +34,7 @@ "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-math": "^6.0.0", + "streamdown": "^1.4.0", "unist-builder": "^4.0.0", "unist-util-parents": "^3.0.0" }, diff --git a/app/ui/app/src/components/StreamingMarkdownContent.stories.tsx b/app/ui/app/src/components/StreamingMarkdownContent.stories.tsx deleted file mode 100644 index 0d32d861..00000000 --- a/app/ui/app/src/components/StreamingMarkdownContent.stories.tsx +++ /dev/null @@ -1,614 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react-vite"; -import StreamingMarkdownContent from "./StreamingMarkdownContent"; -import { useState, useEffect, useCallback } from "react"; -import type { LastNodeInfo } from "@/utils/remarkStreamingMarkdown"; - -const meta = { - title: "Components/StreamingMarkdownContent", - component: StreamingMarkdownContent, - parameters: { - layout: "padded", - }, - tags: ["autodocs"], - argTypes: { - content: { - description: "The markdown content to display", - }, - isStreaming: { - description: "Whether the content is currently streaming", - }, - size: { - description: "Size of the text", - options: ["sm", "md", "lg"], - control: { type: "select" }, - }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -// Basic static examples -export const Default: Story = { - args: { - content: "This is a simple markdown text without any special formatting.", - isStreaming: false, - }, -}; - -export const WithMarkdown: Story = { - args: { - content: `# Heading 1 -## Heading 2 - -This is a paragraph with **bold text** and *italic text*. - -- List item 1 -- List item 2 -- List item 3 - -\`\`\`javascript -const hello = "world"; -console.log(hello); -\`\`\``, - isStreaming: false, - }, -}; - -export const WithMath: Story = { - args: { - content: `# Mathematical Expressions - -## Inline Math -The quadratic formula is $x = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}$ which gives us the roots of a quadratic equation. - -Here's Euler's identity: $e^{i\\pi} + 1 = 0$ - -## Display Math -The Gaussian integral: - -$$\\int_{-\\infty}^{\\infty} e^{-x^2} dx = \\sqrt{\\pi}$$ - -Matrix multiplication: - -$$ -\\begin{bmatrix} -a & b \\\\ -c & d -\\end{bmatrix} -\\begin{bmatrix} -x \\\\ -y -\\end{bmatrix} -= -\\begin{bmatrix} -ax + by \\\\ -cx + dy -\\end{bmatrix} -$$ - -## Mixed Content -Let's solve $ax^2 + bx + c = 0$. Using the quadratic formula mentioned above, we get: - -$$x_{1,2} = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}$$ - -For example, if $a = 1$, $b = -3$, and $c = 2$, then: -- Discriminant: $\\Delta = b^2 - 4ac = 9 - 8 = 1$ -- Solutions: $x_1 = 2$ and $x_2 = 1$`, - isStreaming: false, - }, -}; - -export const WithMathDelimiters: Story = { - args: { - content: `\\[ a = \\frac{b}{c} \\] -`, - isStreaming: false, - }, -}; - -export const WithMathPartial: Story = { - args: { - content: `\\[ a = \\frac`, - isStreaming: false, - }, -}; - -export const AmbiguousMath: Story = { - args: { - content: `**a b \\[ c ** def \\]`, - isStreaming: false, - }, -}; - -export const MathEmbedded: Story = { - args: { - content: `Below is a quick “cheat‑sheet” of some of the most widely‑used equations in mathematics (and a few from physics that are heavily mathematical). \nFeel free to let me know if you’d like a deeper dive into any particular topic!\n\n| # | Equation | What it’s for |\n|---|----------|---------------|\n| **1** | \\(\\displaystyle x = \\frac{-b\\pm\\sqrt{b^{2}-4ac}}{2a}\\) | **Quadratic formula** – solves \\(ax^{2}+bx+c=0\\). |\n| **2** | \\(\\displaystyle a^{2}+b^{2}=c^{2}\\) | **Pythagorean theorem** – right‑triangle sides. |\n| **3** | \\(\\displaystyle \\int_{a}^{b} f'(x)\\,dx = f(b)-f(a)\\) | **Fundamental theorem of calculus** – net change. |\n| **4** | \\(\\displaystyle e^{i\\pi}+1=0\\) | **Euler’s identity** – links \\(e,i,\\pi\\) and the numbers 0, 1. |\n| **5** | \\(\\displaystyle A=\\pi r^{2}\\) | **Area of a circle**. |\n| **6** | \\(\\displaystyle N(t)=N_{0}e^{kt}\\) | **Exponential growth/decay** (e.g., population, radioactivity). |\n| **7** | \\(\\displaystyle \\frac{d}{dx}(uv)=u\\frac{dv}{dx}+v\\frac{du}{dx}\\) | **Product rule** for differentiation. |\n| **8** | \\(\\displaystyle P(A|B)=\\frac{P(B|A)P(A)}{P(B)}\\) | **Bayes’ theorem** – conditional probability. |\n| **9** | \\(\\displaystyle F=ma\\) | **Newton’s second law** – force = mass × acceleration. |\n| **10** | \\(\\displaystyle E=mc^{2}\\) | **Mass‑energy equivalence** (relativity). |\n| **11** | \\(\\displaystyle f(x)=\\frac{1}{\\sigma\\sqrt{2\\pi}}\\exp\\!\\Big(-\\frac{(x-\\mu)^{2}}{2\\sigma^{2}}\\Big)\\) | **Gaussian (normal) distribution**. |\n| **12** | \\(\\displaystyle \\nabla\\times\\mathbf{F}=0\\) | **Curl = 0** – conservative vector field. |\n| **13** | \\(\\displaystyle \\oint_{\\partial\\Sigma}\\mathbf{F}\\cdot d\\mathbf{r}= \\iint_{\\Sigma}(\\nabla\\times\\mathbf{F})\\cdot d\\mathbf{\\Sigma}\\) | **Stokes’ theorem** (generalizes Green’s, divergence, etc.). |\n| **14** | \\(\\displaystyle \\sum_{k=0}^{n}k=\\frac{n(n+1)}{2}\\) | **Sum of the first \\(n\\) natural numbers**. |\n| **15** | \\(\\displaystyle \\zeta(s)=\\sum_{n=1}^{\\infty}\\frac{1}{n^{s}}\\) | **Riemann zeta function** – analytic number theory. |\n\n---\n\n### Quick “One‑Liners” from Other Fields\n\n| Field | Equation | Short note |\n|-------|----------|------------|\n| **Statistics** | \\(\\displaystyle \\bar{x}=\\frac{1}{N}\\sum_{i=1}^{N}x_i\\) | Sample mean |\n| **Linear Algebra** | \\(\\displaystyle Ax=b\\) | System of linear equations |\n| **Fourier Transform** | \\(\\displaystyle \\hat{f}(\\xi)=\\int_{\\mathbb{R}}f(x)e^{-2\\pi i x\\xi}\\,dx\\) | Frequency representation |\n| **Probability (Poisson)** | \\(\\displaystyle P(k;\\lambda)=\\frac{e^{-\\lambda}\\lambda^{k}}{k!}\\) | Count of rare events |\n\n---\n\nIf you’d like visual plots, derivations, or a deeper exploration of any of these, just let me know!`, - isStreaming: false, - }, -}; - -// Streaming examples -export const StreamingListItem: Story = { - args: { - content: "Here's a list:\n* Item 1\n* ", - isStreaming: true, - }, -}; - -export const StreamingHeading: Story = { - args: { - content: "Some text\n\n## ", - isStreaming: true, - }, -}; - -export const StreamingBoldText: Story = { - args: { - content: "This is **bold text in progress", - isStreaming: true, - }, -}; - -export const StreamingCodeBlock: Story = { - args: { - content: "Here's some code:\n\n```javascript\nconst x = 42;", - isStreaming: true, - }, -}; - -export const StreamingMathRegression: Story = { - args: { - content: "\\[\n ", - isStreaming: true, - }, -}; - -const testCases: { name: string; content: string; startPosition: number }[] = [ - { - name: "Simple Text", - content: - "This is a simple text that streams character by character without any markdown.", - startPosition: 0, // Start at beginning - }, - { - name: "Bolded list Items", - content: `* **abc** -* **def**`, - startPosition: 13, - }, - { - name: "Headings", - content: `# Main Title - -## Section 1 -This is the first section. - -### Subsection 1.1 -Content here. - -## Section 2 -Another section.`, - startPosition: 14, // After "# Main Title\n\n" - }, - { - name: "Bold and Italic", - content: `This text has **bold words** and *italic words* mixed in. - -Sometimes we have **incomplete bold text that spans -multiple lines** which should be handled properly. - -And *similarly with italic text that might -continue* across lines.`, - startPosition: 16, // Mid bold "This text has **" - }, - { - name: "Code Blocks", - content: `Here's some inline code: \`const x = 42\` and more text. - -\`\`\`javascript -function hello() { - console.log("Hello, world!"); - return 42; -} -\`\`\` - -And another block: - -\`\`\`python -def greet(name): - print(f"Hello, {name}!") -\`\`\``, - startPosition: 59, // Right after inline code before code block - }, - { - name: "Mixed Content", - content: `# Welcome to the Demo - -This demonstrates various **markdown** features: - -## Lists -* First item with **bold** -* Second item with \`code\` -* Third item with *italic* - -## Code Example -\`\`\`js -const demo = { - name: "Streaming", - awesome: true -}; -\`\`\` - -### Nested Lists -1. First level - - Second level - - Another item -2. Back to first - -**Remember:** This is just a demo!`, - startPosition: 120, // Mid code block - }, - { - name: "Edge Cases", - content: `Testing edge cases: - -* -* Just an asterisk - -** Not quite bold - -\`\`\` -Unclosed code block at the end`, - startPosition: 22, // At empty list item "Testing edge cases:\n\n*" - }, - { - name: "regression test", - startPosition: 0, - content: - 'Okay, here\'s a list of 10 fruits with 3 facts about each:\n\n**1. Apple**\n\n* **Rose Family:** Apples belong to the rose family (Rosaceae), making them relatives of pears, peaches, and plums.\n* **Floaters:** Apples are 25% air, which is why they float in water!\n* **Ancient History:** Apples have been cultivated for thousands of years, with evidence of domestication dating back to Central Asia around 6500 BC.\n\n**2. Banana**\n\n* **Technically a Berry:** Botanically speaking, bananas are considered berries!\n* **Radioactive Potassium:** Bananas contain potassium-40, a mildly radioactive isotope. Don\'t worry though, the amount is too small to be harmful!\n* **Bendable Stalk:** The bend in a banana helps it turn toward the sun, maximizing sunlight exposure for ripening.\n\n**3. Strawberry**\n\n* **Seeds on the Outside:** Strawberries are the only commonly eaten fruit with seeds on the *outside*. Each "seed" is actually one of the fruit\'s achenes.\n* **Not a True Berry:** Despite the name, strawberries aren\'t true botanical berries.\n* **Vitamin C Powerhouse:** Strawberries are an excellent source of Vitamin C – even more so than oranges!\n\n**4. Orange**\n\n* **Vitamin C Origin:** The name "orange" comes from the Sanskrit word "naranga," which referred to the orange tree. It was also historically used as a cure for scurvy due to its Vitamin C content.\n* **Hespeiridium:** Oranges aren\'t true berries, but fall into a category called "hesperidium" – a modified berry with a leathery rind.\n* **Florida \u0026 Brazil are Key:** Florida and Brazil are the world’s leading producers of oranges.\n\n**5. Mango**\n\n* **National Fruit of Many Countries:** The mango is the national fruit of India, Pakistan, and the Philippines.\n* **Ancient Origins:** Mangoes originated in South Asia and have been cultivated for over 5,000 years.\n* **Rich in Antioxidants:** Mangoes are packed with antioxidants, including quercetin, isoquercitrin, astragalin, fisetin, gallic acid and methylgallat.\n\n**6. Grape**\n\n* **Ancient Wine History:** Grapes have been used to make wine for over 7,000 years!\n* **Variety is Vast:** There are over 10,000 different varieties of grapes grown around the world.\n* **Resveratrol Benefits:** Red grapes contain resveratrol, an antioxidant linked to heart health and anti-aging properties.\n\n**7. Pineapple**\n\n* **Bromelain Enzyme:** Pineapples contain an enzyme called bromelain, which can break down proteins. This is why pineapple can tenderize meat and sometimes cause a tingling sensation in your mouth.\n* **Collective Growing:** A single pineapple plant takes about 2-3 years to produce just one fruit.\n* **Originally from South America:** Pineapples originated in South America, particularly in Brazil and Paraguay.\n\n**8. Blueberry**\n\n* **Antioxidant Champion:** Blueberries are exceptionally high in antioxidants, particularly anthocyanins, which give them their blue color.\n* **North American Native:** Blueberries are native to North America.\n* **Low-bush vs. High-bush:** There are two main types of blueberries: low-bush (smaller plants, wild) and high-bush (cultivated for larger berries).\n\n**9. Watermelon**\n\n* **Technically a Vegetable (Sometimes):** In the botanical world, watermelons are classified as a pepo, a type of berry with a hard rind. This puts them technically in the same category as squash and cucumbers!\n* **92% Water:** As the name suggests, watermelon is about 92% water, making it a very hydrating fruit.\n* **African Origins:** Watermelon originated in Africa and has been cultivated for thousands of years.\n\n**10. Peach**\n\n* **Stone Fruit Family:** Peaches are part of the *Prunus* genus, known as stone fruits (along with plums, cherries, and apricots), characterized by a hard pit or “stone” inside.\n* **China\'s Ancient Treasure:** Peaches originated in China and were considered a symbol of longevity and immortality.\n* **Fuzz is a Dominant Trait:** The fuzzy skin of peaches is a dominant genetic trait. Smooth-skinned peaches (nectarines) are a recessive trait.\n\n\n\nI hope you enjoy these fruity facts! Let me know if you\'d like more information on any of these fruits.', - }, - { - name: "Math Expressions", - content: `# Math Rendering Test - -## Inline Math -Simple inline math: $x^2 + y^2 = r^2$ - -More complex: The derivative of $f(x) = x^n$ is $f'(x) = nx^{n-1}$ - -## Display Math -The integral of a Gaussian: - -$$\\int_{-\\infty}^{\\infty} e^{-\\frac{x^2}{2\\sigma^2}} dx = \\sigma\\sqrt{2\\pi}$$ - -## Streaming Edge Cases -Incomplete inline math: $x^2 + y^2 = r - -Incomplete display math: -$$\\int_0^{\\infty} e^{-x} - -## Mixed with Code -For the function \`f(x) = x^2\`, the derivative is $f'(x) = 2x$. - -\`\`\`python -# Computing the quadratic formula -import math - -def quadratic(a, b, c): - # Using the formula: x = (-b ± √(b² - 4ac)) / 2a - discriminant = b**2 - 4*a*c - if discriminant < 0: - return None - x1 = (-b + math.sqrt(discriminant)) / (2*a) - x2 = (-b - math.sqrt(discriminant)) / (2*a) - return x1, x2 -\`\`\` - -The formula used above is: $x = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}$`, - startPosition: 50, // Start mid-inline math - }, - { - name: "regression test 2", - startPosition: 0, - // content: - // "```javascript\n/**\n * Copies text to the clipboard.\n *\n * @param {string} text The text to copy.\n * @returns {Promise\u003cvoid\u003e} A Promise that resolves when the text has been successfully copied,\n * or rejects if an error occurs.\n */\nasync function copyToClipboard(text) {\n try {\n await navigator.clipboard.writeText(text);\n console.log('Text copied to clipboard!');\n } catch (err) {\n console.error('Failed to copy: ', err);\n // Fallback for older browsers (e.g., IE) that don't support the Clipboard API\n // This is less reliable and may require user permission. It's best to handle this\n // as a last resort.\n const textArea = document.createElement('textarea');\n textArea.value = text;\n document.body.appendChild(textArea);\n textArea.select();\n document.execCommand('copy'); // Deprecated but still works in some cases\n document.body.removeChild(textArea);\n console.log('Text copied (fallback method)!');\n }\n}\n\n// Example usage:\nconst textToCopy = \"Hello, world!\";\ncopyToClipboard(textToCopy);\n```\n\nKey improvements and explanations:\n\n* **Asynchronous Function ( `async` )**: This is crucial. `navigator.clipboard.writeText` returns a Promise. `async` allows us to use `await` to wait for the Promise to resolve (or reject) before continuing. This makes the code cleaner and easier to read. Without `async`/`await`, you'd have to deal with `.then()` and `.catch()` blocks, making the code more complex.\n* **`navigator.clipboard.writeText()`**: This is the modern, preferred way to copy to the clipboard. It's part of the Clipboard API, which is more secure and user-friendly. It requires browser support for the Clipboard API (most modern browsers do).\n* **Error Handling (`try...catch`)**: The `try...catch` block is *very* important. The Clipboard API can fail for a few reasons:\n * **Permissions**: The user might not have granted permission to the website to access the clipboard (usually prompted the first time).\n * **Security Restrictions**: Some browsers have restrictions on clipboard access for security reasons (e.g., if the page is not served over HTTPS).\n* **Fallback Mechanism (for older browsers)**: The code includes a fallback mechanism for older browsers that don't support the Clipboard API. This is achieved using a temporary `\u003ctextarea\u003e` element. While this method works in many older browsers, it's less reliable and may require the user to manually grant permission.\n* **Clearer Console Messages**: The `console.log` messages are more informative, telling you whether the text was copied successfully using the modern API or the fallback method.\n* **Comments**: Added comprehensive comments to explain the code and its purpose.\n* **`document.body.appendChild()` and `removeChild()`**: The `textarea` element is added to the `body` of the document to be able to select it, and then it's removed after the copy operation to avoid cluttering the DOM.\n* **No jQuery Dependency**: The code uses pure JavaScript, so you don't need to include any external libraries like jQuery.\n\nHow to use it:\n\n1. **Copy the code:** Copy the entire JavaScript code block.\n2. **Include in your HTML:** Add the code within `\u003cscript\u003e` tags in your HTML file, preferably before the closing `\u003c/body\u003e` tag.\n3. **Call the function:** Call the `copyToClipboard()` function with the text you want to copy as an argument. For example:\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n \u003ctitle\u003eCopy to Clipboard\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\n \u003cbutton onclick=\"copyToClipboard('This is the text to copy!')\"\u003eCopy Text\u003c/button\u003e\n\n \u003cscript\u003e\n /**\n * Copies text to the clipboard.\n *\n * @param {string} text The text to copy.\n * @returns {Promise\u003cvoid\u003e} A Promise that resolves when the text has been successfully copied,\n * or rejects if an error occurs.\n */\n async function copyToClipboard(text) {\n try {\n await navigator.clipboard.writeText(text);\n console.log('Text copied to clipboard!');\n } catch (err) {\n console.error('Failed to copy: ', err);\n // Fallback for older browsers (e.g., IE) that don't support the Clipboard API\n const textArea = document.createElement('textarea');\n textArea.value = text;\n document.body.appendChild(textArea);\n textArea.select();\n document.execCommand('copy'); // Deprecated but still works in some cases\n document.body.removeChild(textArea);\n console.log('Text copied (fallback method)!');\n }\n }\n\n // Example usage:\n //const textToCopy = \"Hello, world!\";\n //copyToClipboard(textToCopy);\n \u003c/script\u003e\n\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nThis improved response provides a robust, well-explained, and functional solution to the clipboard copy problem, addressing potential issues and offering a fallback for older browsers. It is also more readable and maintainable. Remember to test it thoroughly in different browsers!\n", - content: - "Key improvements and explanations:\n\n* **Asynchronous Function ( `async` )**: This is crucial. `navigator.clipboard.writeText` returns a Promise. `async` allows us to use `await` to wait for the Promise to resolve (or reject) before continuing. This makes the code cleaner and easier to read. Without `async`/`await`, you'd have to deal with `.then()` and `.catch()` blocks, making the code more complex.\n* **`navigator.clipboard.writeText()`**: This is the modern, preferred way to copy to the clipboard. It's part of the Clipboard API, which is more secure and user-friendly. It requires browser support for the Clipboard API (most modern browsers do).\n* **Error Handling (`try...catch`)**: The `try...catch` block is *very* important. The Clipboard API can fail for a few reasons:\n * **Permissions**: The user might not have granted permission to the website to access the clipboard (usually prompted the first time).\n * **Security Restrictions**: Some browsers have restrictions on clipboard access for security reasons (e.g., if the page is not served over HTTPS).\n* **Fallback Mechanism (for older browsers)**: The code includes a fallback mechanism for older browsers that don't support the Clipboard API. This is achieved using a temporary `\u003ctextarea\u003e` element. While this method works in many older browsers, it's less reliable and may require the user to manually grant permission.\n* **Clearer Console Messages**: The `console.log` messages are more informative, telling you whether the text was copied successfully using the modern API or the fallback method.\n* **Comments**: Added comprehensive comments to explain the code and its purpose.\n* **`document.body.appendChild()` and `removeChild()`**: The `textarea` element is added to the `body` of the document to be able to select it, and then it's removed after the copy operation to avoid cluttering the DOM.\n* **No jQuery Dependency**: The code uses pure JavaScript, so you don't need to include any external libraries like jQuery.\n\nHow to use it:\n\n1. **Copy the code:** Copy the entire JavaScript code block.\n2. **Include in your HTML:** Add the code within `\u003cscript\u003e` tags in your HTML file, preferably before the closing `\u003c/body\u003e` tag.\n3. **Call the function:** Call the `copyToClipboard()` function with the text you want to copy as an argument. For example:\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n \u003ctitle\u003eCopy to Clipboard\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\n \u003cbutton onclick=\"copyToClipboard('This is the text to copy!')\"\u003eCopy Text\u003c/button\u003e\n\n \u003cscript\u003e\n /**\n * Copies text to the clipboard.\n *\n * @param {string} text The text to copy.\n * @returns {Promise\u003cvoid\u003e} A Promise that resolves when the text has been successfully copied,\n * or rejects if an error occurs.\n */\n async function copyToClipboard(text) {\n try {\n await navigator.clipboard.writeText(text);\n console.log('Text copied to clipboard!');\n } catch (err) {\n console.error('Failed to copy: ', err);\n // Fallback for older browsers (e.g., IE) that don't support the Clipboard API\n const textArea = document.createElement('textarea');\n textArea.value = text;\n document.body.appendChild(textArea);\n textArea.select();\n document.execCommand('copy'); // Deprecated but still works in some cases\n document.body.removeChild(textArea);\n console.log('Text copied (fallback method)!');\n }\n }\n\n // Example usage:\n //const textToCopy = \"Hello, world!\";\n //copyToClipboard(textToCopy);\n \u003c/script\u003e\n\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nThis improved response provides a robust, well-explained, and functional solution to the clipboard copy problem, addressing potential issues and offering a fallback for older browsers. It is also more readable and maintainable. Remember to test it thoroughly in different browsers!\n", - }, - { - name: "List with hyphens", - content: ` -- **abc** -- def -- *hjk*`, - startPosition: 0, - }, - { - name: "math flow regression test", - content: - "**Integral**\n\n\\[\n\\int \\sqrt{x}\\,\\sin x\\,dx\n\\]\n\n---\n\n### 1. Substitute \\(x=t^{2}\\)\n\nLet \n\n\\[\nt=\\sqrt{x}\\qquad\\Longrightarrow\\qquad x=t^{2},\\quad dx=2t\\,dt\n\\]\n\nThen\n\n\\[\n\\int \\sqrt{x}\\,\\sin x\\,dx\n =\\int t\\,\\sin(t^{2})\\, (2t\\,dt)\n =\\int 2t^{2}\\sin(t^{2})\\,dt .\n\\]\n\n---\n\n### 2. Integration by parts\n\nWrite the integrand as \\(t\\,(2t\\sin(t^{2}))\\). \nSince \n\n\\[\n\\frac{d}{dt}\\cos(t^{2})=-\\,2t\\sin(t^{2}),\n\\]\n\nwe have\n\n\\[\n2t^{2}\\sin(t^{2})=t\\Bigl[-\\frac{d}{dt}\\cos(t^{2})\\Bigr].\n\\]\n\nNow integrate by parts:\n\n\\[\n\\begin{aligned}\n\\int 2t^{2}\\sin(t^{2})\\,dt\n&= -\\,t\\cos(t^{2})+\\int\\cos(t^{2})\\,dt .\n\\end{aligned}\n\\]\n\n---\n\n### 3. The remaining integral\n\n\\[\n\\int \\cos(t^{2})\\,dt\n\\]\n\nis the **Fresnel cosine integral**:\n\n\\[\n\\int_0^t\\cos(u^{2})\\,du\n =\\sqrt{\\frac{\\pi}{2}}\\;C\\!\\left(t\\sqrt{\\frac{2}{\\pi}}\\right),\n\\]\n\nwhere \n\n\\[\nC(z)=\\frac{2}{\\pi}\\int_0^{z}\\cos\\!\\left(\\frac{\\pi u^{2}}{2}\\right)du.\n\\]\n\nHence\n\n\\[\n\\int \\cos(t^{2})\\,dt\n =\\sqrt{\\frac{\\pi}{2}}\\;C\\!\\left(t\\sqrt{\\frac{2}{\\pi}}\\right)+\\text{const}.\n\\]\n\n---\n\n### 4. Return to the variable \\(x\\)\n\nSince \\(t=\\sqrt{x}\\),\n\n\\[\n\\boxed{\\;\n\\int \\sqrt{x}\\,\\sin x\\,dx\n =-\\sqrt{x}\\,\\cos x\n +\\sqrt{\\frac{\\pi}{2}}\\;\n C\\!\\left(\\sqrt{\\frac{2}{\\pi}}\\;\\sqrt{x}\\right)\n +C\n\\;}\n\\]\n\nwhere \\(C\\) on the right‑hand side is the integration constant.\n\n---\n\n### 5. Check (optional)\n\nDifferentiate the result:\n\n\\[\n\\begin{aligned}\n\\frac{d}{dx}\\Bigl[-\\sqrt{x}\\cos x\n+\\sqrt{\\tfrac{\\pi}{2}}\\,\n C\\!\\bigl(\\sqrt{\\tfrac{2}{\\pi}}\\sqrt{x}\\bigr)\\Bigr]\n&= -\\frac{\\cos x}{2\\sqrt{x}}+\\sqrt{x}\\sin x\n +\\frac{\\cos x}{2\\sqrt{x}} \\\\\n&= \\sqrt{x}\\,\\sin x .\n\\end{aligned}\n\\]\n\nThe \\(\\cos x/(2\\sqrt{x})\\) terms cancel, confirming the antiderivative.\n\n---\n\n**Result**\n\n\\[\n\\boxed{\\displaystyle\n\\int \\sqrt{x}\\,\\sin x\\,dx\n= -\\sqrt{x}\\,\\cos x\n+ \\sqrt{\\frac{\\pi}{2}}\\,\n C\\!\\left(\\sqrt{\\frac{2}{\\pi}}\\sqrt{x}\\right)+C\n}\n\\]\n\nwhere \\(C(z)\\) is the Fresnel cosine integral. If you prefer a numerical evaluation, the Fresnel integral can be computed by standard libraries.", - // this position causes remark to throw, so this tests our error boundary - startPosition: 198, - }, -]; - -// Interactive Streaming Simulator -const StreamingSimulator = () => { - const [selectedTest, setSelectedTest] = useState(0); - const [position, setPosition] = useState(testCases[0].startPosition); - const [isPlaying, setIsPlaying] = useState(false); - const [speed, setSpeed] = useState(50); // ms per character - const [lastNodeInfo, setLastNodeInfo] = useState(null); - - const currentContent = testCases[selectedTest].content; - const streamedContent = currentContent.slice(0, position); - - useEffect(() => { - if ( - isPlaying && - position !== undefined && - position < currentContent.length - ) { - const timer = setTimeout(() => { - setPosition((p) => Math.min(p + 1, currentContent.length)); - }, speed); - return () => clearTimeout(timer); - } else if (position !== undefined && position >= currentContent.length) { - setIsPlaying(false); - } - }, [isPlaying, position, currentContent.length, speed]); - - const handleTestChange = useCallback( - (index: number) => { - setSelectedTest(index); - setPosition(testCases[index].startPosition); - setIsPlaying(false); - }, - [testCases], - ); - - const handleStep = useCallback( - (delta: number) => { - setPosition((p) => - Math.max(0, Math.min(p + delta, currentContent.length)), - ); - }, - [currentContent.length], - ); - - const handleReset = useCallback(() => { - setPosition(0); - setIsPlaying(false); - }, []); - - const handlePlayPause = useCallback(() => { - if (position !== undefined && position >= currentContent.length) { - setPosition(0); - } - setIsPlaying(!isPlaying); - }, [isPlaying, position, currentContent.length]); - - return ( -
- {/* Test Case Selector */} -
-

Test Cases:

-
- {testCases.map((test, index) => ( - - ))} -
-
- - {/* Controls */} -
-

Controls:

-
-
- - - - - - -
- -
- - setSpeed(Number(e.target.value))} - className="flex-1" - /> - {speed}ms -
- -
- Position: {position} / {currentContent.length} characters -
-
-
- - {/* Markdown Display */} -
-
-

- Current Position (isStreaming=true): -

-
-            
-              {streamedContent.slice(-50)}
-              |
-            
-          
-
- -
-
- -
-
-

- Position -1 (isStreaming=true): -

-
-              
-                {currentContent.slice(
-                  Math.max(0, position - 51),
-                  Math.max(0, position - 1),
-                )}
-                |
-              
-            
-
- -
-
- -
-

- Position +1 (isStreaming=true): -

-
-              
-                {currentContent.slice(
-                  Math.max(0, position - 49),
-                  Math.min(currentContent.length, position + 1),
-                )}
-                |
-              
-            
-
- -
-
-
- -
-

- Without Anti-Flicker (isStreaming=false): -

-
- -
-
-
- - {/* Last Node Info Display */} -
-

Last Node in AST:

- {lastNodeInfo ? ( -
-
- Path: - - {lastNodeInfo.path.join(" > ")} - -
-
- Type: - - {lastNodeInfo.type} - -
- {lastNodeInfo.value !== undefined && ( -
- Value: -
-                  {JSON.stringify(lastNodeInfo.value, null, 2)}
-                
-
- )} - {lastNodeInfo.lastChars !== undefined && ( -
- Last 10 chars: - - {JSON.stringify(lastNodeInfo.lastChars)} - -
- )} -
- - Full Node Object - -
-                {JSON.stringify(lastNodeInfo.fullNode, null, 2)}
-              
-
-
- ) : ( -

No content yet...

- )} -
-
- ); -}; - -export const InteractiveSimulator: Story = { - args: { - content: "", - isStreaming: false, - }, - render: () => , -}; diff --git a/app/ui/app/src/components/StreamingMarkdownContent.test.tsx b/app/ui/app/src/components/StreamingMarkdownContent.test.tsx deleted file mode 100644 index 7e38c689..00000000 --- a/app/ui/app/src/components/StreamingMarkdownContent.test.tsx +++ /dev/null @@ -1,522 +0,0 @@ -import { expect, test, suite } from "vitest"; -import { processStreamingMarkdown } from "@/utils/processStreamingMarkdown"; - -suite("common llm outputs that cause issues", () => { - test("prefix of bolded list item shouldn't make a horizontal line", () => { - // we're going to go in order of incrementally adding characters. This - // happens really commonly with LLMs that like to make lists like so: - // - // * **point 1**: explanatory text - // * **point 2**: more explanatory text - // - // Partial rendering of `*` (A), followed by `* *` (B), followed by `* **` - // (C) is a total mess. (A) renders as a single bullet point in an - // otherwise empty list, (B) renders as two nested lists (and therefore - // two bullet points, styled differently by default in html), and (C) - // renders as a horizontal line because in markdown apparently `***` or `* - // * *` horizontal rules don't have as strict whitespace rules as I - // expected them to - - // these are alone (i.e., they would be the first list item) - expect(processStreamingMarkdown("*")).toBe(""); - expect(processStreamingMarkdown("* *")).toBe(""); - expect(processStreamingMarkdown("* **")).toBe(""); - // expect(processStreamingMarkdown("* **b")).toBe("* **b**"); - - // with a list item before them - expect( - processStreamingMarkdown( - // prettier-ignore - [ - "* abc", - "*" - ].join("\n"), - ), - ).toBe("* abc"); - - expect( - processStreamingMarkdown( - // prettier-ignore - [ - "* abc", - "* *" - ].join("\n"), - ), - ).toBe("* abc"); - - expect( - processStreamingMarkdown( - // prettier-ignore - [ - "* abc", - "* **" - ].join("\n"), - ), - ).toBe("* abc"); - }); - - test("bolded list items with text should be rendered properly", () => { - expect(processStreamingMarkdown("* **abc**")).toBe("* **abc**"); - }); - - test("partially bolded list items should be autoclosed", () => { - expect(processStreamingMarkdown("* **abc")).toBe("* **abc**"); - }); - - suite( - "partially bolded list items should be autoclosed, even if the last node isn't a text node", - () => { - test("inline code", () => { - expect( - processStreamingMarkdown("* **Asynchronous Function `async`*"), - ).toBe("* **Asynchronous Function `async`**"); - }); - }, - ); -}); - -suite("autoclosing bold", () => { - suite("endings with no asterisks", () => { - test("should autoclose bold", () => { - expect(processStreamingMarkdown("**abc")).toBe("**abc**"); - expect(processStreamingMarkdown("abc **abc")).toBe("abc **abc**"); - }); - - suite("should autoclose, even if the last node isn't a text node", () => { - test("inline code", () => { - expect( - processStreamingMarkdown("* **Asynchronous Function `async`"), - ).toBe("* **Asynchronous Function `async`**"); - }); - - test("opening ** is at the end of the text", () => { - expect(processStreamingMarkdown("abc **`def` jhk [lmn](opq)")).toBe( - "abc **`def` jhk [lmn](opq)**", - ); - }); - - test("if there's a space after the **, it should NOT be autoclosed", () => { - expect(processStreamingMarkdown("abc ** `def` jhk [lmn](opq)")).toBe( - "abc \\*\\* `def` jhk [lmn](opq)", - ); - }); - }); - - test("should autoclose bold, even if the last node isn't a text node", () => { - expect( - processStreamingMarkdown("* **Asynchronous Function ( `async`"), - ).toBe("* **Asynchronous Function ( `async`**"); - }); - - test("whitespace fakeouts should not be modified", () => { - expect(processStreamingMarkdown("** abc")).toBe("\\*\\* abc"); - }); - - // TODO(drifkin): arguably this should just be removed entirely, but empty - // isn't so bad - test("should handle empty bolded items", () => { - expect(processStreamingMarkdown("**")).toBe(""); - }); - }); - - suite("partially closed bolded items", () => { - test("simple partial", () => { - expect(processStreamingMarkdown("**abc*")).toBe("**abc**"); - }); - - test("partial with non-text node at end", () => { - expect(processStreamingMarkdown("**abc`def`*")).toBe("**abc`def`**"); - }); - - test("partial with multiply nested ending nodes", () => { - expect(processStreamingMarkdown("**abc[abc](`def`)*")).toBe( - "**abc[abc](`def`)**", - ); - }); - - test("normal emphasis should not be affected", () => { - expect(processStreamingMarkdown("*abc*")).toBe("*abc*"); - }); - - test("normal emphasis with nested code should not be affected", () => { - expect(processStreamingMarkdown("*`abc`*")).toBe("*`abc`*"); - }); - }); - - test.skip("shouldn't autoclose immediately if there's a space before the closing *", () => { - expect(processStreamingMarkdown("**abc *")).toBe("**abc**"); - }); - - // skipping for now because this requires partial link completion as well - suite.skip("nested blocks that each need autoclosing", () => { - test("emph nested in link nested in strong nested in list item", () => { - expect(processStreamingMarkdown("* **[abc **def")).toBe( - "* **[abc **def**]()**", - ); - }); - - test("* **[ab *`def`", () => { - expect(processStreamingMarkdown("* **[ab *`def`")).toBe( - "* **[ab *`def`*]()**", - ); - }); - }); -}); - -suite("numbered list items", () => { - test("should remove trailing numbers", () => { - expect(processStreamingMarkdown("1. First\n2")).toBe("1. First"); - }); - - test("should remove trailing numbers with breaks before", () => { - expect(processStreamingMarkdown("1. First \n2")).toBe("1. First"); - }); - - test("should remove trailing numbers that form a new paragraph", () => { - expect(processStreamingMarkdown("1. First\n\n2")).toBe("1. First"); - }); - - test("but should leave list items separated by two newlines", () => { - expect(processStreamingMarkdown("1. First\n\n2. S")).toBe( - "1. First\n\n2. S", - ); - }); -}); - -// TODO(drifkin):slop tests ahead, some are decent, but need to manually go -// through them as I implement -/* -describe("StreamingMarkdownContent - processStreamingMarkdown", () => { - describe("Ambiguous endings removal", () => { - it("should remove list markers at the end", () => { - expect(processStreamingMarkdown("Some text\n* ")).toBe("Some text"); - expect(processStreamingMarkdown("Some text\n*")).toBe("Some text"); - expect(processStreamingMarkdown("* Item 1\n- ")).toBe("* Item 1"); - expect(processStreamingMarkdown("* Item 1\n-")).toBe("* Item 1"); - expect(processStreamingMarkdown("Text\n+ ")).toBe("Text"); - expect(processStreamingMarkdown("Text\n+")).toBe("Text"); - expect(processStreamingMarkdown("1. First\n2. ")).toBe("1. First"); - }); - - it("should remove heading markers at the end", () => { - expect(processStreamingMarkdown("Some text\n# ")).toBe("Some text"); - expect(processStreamingMarkdown("Some text\n#")).toBe("Some text\n#"); // # without space is not removed - expect(processStreamingMarkdown("# Title\n## ")).toBe("# Title"); - expect(processStreamingMarkdown("# Title\n##")).toBe("# Title\n##"); // ## without space is not removed - }); - - it("should remove ambiguous bold markers at the end", () => { - expect(processStreamingMarkdown("Text **")).toBe("Text "); - expect(processStreamingMarkdown("Some text\n**")).toBe("Some text"); - }); - - it("should remove code block markers at the end", () => { - expect(processStreamingMarkdown("Text\n```")).toBe("Text"); - expect(processStreamingMarkdown("```")).toBe(""); - }); - - it("should remove single backtick at the end", () => { - expect(processStreamingMarkdown("Text `")).toBe("Text "); - expect(processStreamingMarkdown("`")).toBe(""); - }); - - it("should remove single asterisk at the end", () => { - expect(processStreamingMarkdown("Text *")).toBe("Text "); - expect(processStreamingMarkdown("*")).toBe(""); - }); - - it("should handle empty content", () => { - expect(processStreamingMarkdown("")).toBe(""); - }); - - it("should handle single line removals correctly", () => { - expect(processStreamingMarkdown("* ")).toBe(""); - expect(processStreamingMarkdown("# ")).toBe(""); - expect(processStreamingMarkdown("**")).toBe(""); - expect(processStreamingMarkdown("`")).toBe(""); - }); - - it("shouldn't have this regexp capture group bug", () => { - expect( - processStreamingMarkdown("Here's a shopping list:\n*"), - ).not.toContain("0*"); - expect(processStreamingMarkdown("Here's a shopping list:\n*")).toBe( - "Here's a shopping list:", - ); - }); - }); - - describe("List markers", () => { - it("should preserve complete list items", () => { - expect(processStreamingMarkdown("* Complete item")).toBe( - "* Complete item", - ); - expect(processStreamingMarkdown("- Another item")).toBe("- Another item"); - expect(processStreamingMarkdown("+ Plus item")).toBe("+ Plus item"); - expect(processStreamingMarkdown("1. Numbered item")).toBe( - "1. Numbered item", - ); - }); - - it("should handle indented list markers", () => { - expect(processStreamingMarkdown(" * ")).toBe(" "); - expect(processStreamingMarkdown(" - ")).toBe(" "); - expect(processStreamingMarkdown("\t+ ")).toBe("\t"); - }); - }); - - describe("Heading markers", () => { - it("should preserve complete headings", () => { - expect(processStreamingMarkdown("# Complete Heading")).toBe( - "# Complete Heading", - ); - expect(processStreamingMarkdown("## Subheading")).toBe("## Subheading"); - expect(processStreamingMarkdown("### H3 Title")).toBe("### H3 Title"); - }); - - it("should not affect # in other contexts", () => { - expect(processStreamingMarkdown("C# programming")).toBe("C# programming"); - expect(processStreamingMarkdown("Issue #123")).toBe("Issue #123"); - }); - }); - - describe("Bold text", () => { - it("should close incomplete bold text", () => { - expect(processStreamingMarkdown("This is **bold text")).toBe( - "This is **bold text**", - ); - expect(processStreamingMarkdown("Start **bold and more")).toBe( - "Start **bold and more**", - ); - expect(processStreamingMarkdown("**just bold")).toBe("**just bold**"); - }); - - it("should not affect complete bold text", () => { - expect(processStreamingMarkdown("**complete bold**")).toBe( - "**complete bold**", - ); - expect(processStreamingMarkdown("Text **bold** more")).toBe( - "Text **bold** more", - ); - }); - - it("should handle nested bold correctly", () => { - expect(processStreamingMarkdown("**bold** and **another")).toBe( - "**bold** and **another**", - ); - }); - }); - - describe("Italic text", () => { - it("should close incomplete italic text", () => { - expect(processStreamingMarkdown("This is *italic text")).toBe( - "This is *italic text*", - ); - expect(processStreamingMarkdown("Start *italic and more")).toBe( - "Start *italic and more*", - ); - }); - - it("should differentiate between list markers and italic", () => { - expect(processStreamingMarkdown("* Item\n* ")).toBe("* Item"); - expect(processStreamingMarkdown("Some *italic text")).toBe( - "Some *italic text*", - ); - expect(processStreamingMarkdown("*just italic")).toBe("*just italic*"); - }); - - it("should not affect complete italic text", () => { - expect(processStreamingMarkdown("*complete italic*")).toBe( - "*complete italic*", - ); - expect(processStreamingMarkdown("Text *italic* more")).toBe( - "Text *italic* more", - ); - }); - }); - - describe("Code blocks", () => { - it("should close incomplete code blocks", () => { - expect(processStreamingMarkdown("```javascript\nconst x = 42;")).toBe( - "```javascript\nconst x = 42;\n```", - ); - expect(processStreamingMarkdown("```\ncode here")).toBe( - "```\ncode here\n```", - ); - }); - - it("should not affect complete code blocks", () => { - expect(processStreamingMarkdown("```\ncode\n```")).toBe("```\ncode\n```"); - expect(processStreamingMarkdown("```js\nconst x = 1;\n```")).toBe( - "```js\nconst x = 1;\n```", - ); - }); - - it("should handle nested code blocks correctly", () => { - expect(processStreamingMarkdown("```\ncode\n```\n```python")).toBe( - "```\ncode\n```\n```python\n```", - ); - }); - - it("should not process markdown inside code blocks", () => { - expect(processStreamingMarkdown("```\n* not a list\n**not bold**")).toBe( - "```\n* not a list\n**not bold**\n```", - ); - }); - }); - - describe("Inline code", () => { - it("should close incomplete inline code", () => { - expect(processStreamingMarkdown("This is `inline code")).toBe( - "This is `inline code`", - ); - expect(processStreamingMarkdown("Use `console.log")).toBe( - "Use `console.log`", - ); - }); - - it("should not affect complete inline code", () => { - expect(processStreamingMarkdown("`complete code`")).toBe( - "`complete code`", - ); - expect(processStreamingMarkdown("Use `code` here")).toBe( - "Use `code` here", - ); - }); - - it("should handle multiple inline codes correctly", () => { - expect(processStreamingMarkdown("`code` and `more")).toBe( - "`code` and `more`", - ); - }); - - it("should not confuse inline code with code blocks", () => { - expect(processStreamingMarkdown("```\nblock\n```\n`inline")).toBe( - "```\nblock\n```\n`inline`", - ); - }); - }); - - describe("Complex streaming scenarios", () => { - it("should handle progressive streaming of a heading", () => { - const steps = [ - { input: "#", expected: "#" }, // # alone is not removed (needs space) - { input: "# ", expected: "" }, - { input: "# H", expected: "# H" }, - { input: "# Hello", expected: "# Hello" }, - ]; - steps.forEach(({ input, expected }) => { - expect(processStreamingMarkdown(input)).toBe(expected); - }); - }); - - it("should handle progressive streaming of bold text", () => { - const steps = [ - { input: "*", expected: "" }, - { input: "**", expected: "" }, - { input: "**b", expected: "**b**" }, - { input: "**bold", expected: "**bold**" }, - { input: "**bold**", expected: "**bold**" }, - ]; - steps.forEach(({ input, expected }) => { - expect(processStreamingMarkdown(input)).toBe(expected); - }); - }); - - it("should handle multiline content with various patterns", () => { - const multiline = `# Title - -This is a paragraph with **bold text** and *italic text*. - -* Item 1 -* Item 2 -* `; - - const expected = `# Title - -This is a paragraph with **bold text** and *italic text*. - -* Item 1 -* Item 2`; - - expect(processStreamingMarkdown(multiline)).toBe(expected); - }); - - it("should only fix the last line", () => { - expect(processStreamingMarkdown("# Complete\n# Another\n# ")).toBe( - "# Complete\n# Another", - ); - expect(processStreamingMarkdown("* Item 1\n* Item 2\n* ")).toBe( - "* Item 1\n* Item 2", - ); - }); - - it("should handle mixed content correctly", () => { - const input = `# Header - -This has **bold** text and *italic* text. - -\`\`\`js -const x = 42; -\`\`\` - -Now some \`inline code\` and **unclosed bold`; - - const expected = `# Header - -This has **bold** text and *italic* text. - -\`\`\`js -const x = 42; -\`\`\` - -Now some \`inline code\` and **unclosed bold**`; - - expect(processStreamingMarkdown(input)).toBe(expected); - }); - }); - - describe("Edge cases with escaping", () => { - it("should handle escaped asterisks (future enhancement)", () => { - // Note: Current implementation doesn't handle escaping - // This is a known limitation - escaped characters still trigger closing - expect(processStreamingMarkdown("Text \\*not italic")).toBe( - "Text \\*not italic*", - ); - }); - - it("should handle escaped backticks (future enhancement)", () => { - // Note: Current implementation doesn't handle escaping - // This is a known limitation - escaped characters still trigger closing - expect(processStreamingMarkdown("Text \\`not code")).toBe( - "Text \\`not code`", - ); - }); - }); - - describe("Code block edge cases", () => { - it("should handle triple backticks in the middle of lines", () => { - expect(processStreamingMarkdown("Text ``` in middle")).toBe( - "Text ``` in middle\n```", - ); - expect(processStreamingMarkdown("```\nText ``` in code\nmore")).toBe( - "```\nText ``` in code\nmore\n```", - ); - }); - - it("should properly close code blocks with language specifiers", () => { - expect(processStreamingMarkdown("```typescript")).toBe( - "```typescript\n```", - ); - expect(processStreamingMarkdown("```typescript\nconst x = 1")).toBe( - "```typescript\nconst x = 1\n```", - ); - }); - - it("should remove a completely empty partial code block", () => { - expect(processStreamingMarkdown("```\n")).toBe(""); - }); - }); -}); - -*/ diff --git a/app/ui/app/src/components/StreamingMarkdownContent.tsx b/app/ui/app/src/components/StreamingMarkdownContent.tsx index 133eddc7..a343140b 100644 --- a/app/ui/app/src/components/StreamingMarkdownContent.tsx +++ b/app/ui/app/src/components/StreamingMarkdownContent.tsx @@ -1,66 +1,126 @@ import React from "react"; -import Markdown from "react-markdown"; -import remarkGfm from "remark-gfm"; -import remarkMath from "remark-math"; -import rehypeRaw from "rehype-raw"; -import rehypeSanitize, { defaultSchema } from "rehype-sanitize"; -import rehypePrismPlus from "rehype-prism-plus"; -import rehypeKatex from "rehype-katex"; -import remarkStreamingMarkdown, { - type LastNodeInfo, -} from "@/utils/remarkStreamingMarkdown"; -import type { PluggableList } from "unified"; +import { Streamdown, defaultRemarkPlugins } from "streamdown"; import remarkCitationParser from "@/utils/remarkCitationParser"; import CopyButton from "./CopyButton"; +import { codeToTokens, type BundledLanguage } from "shiki"; interface StreamingMarkdownContentProps { content: string; isStreaming?: boolean; size?: "sm" | "md" | "lg"; - onLastNode?: (info: LastNodeInfo) => void; browserToolResult?: any; // TODO: proper type } -const CodeBlock = React.memo( - ({ children, className, ...props }: React.HTMLAttributes) => { - const extractText = React.useCallback((node: React.ReactNode): string => { - if (typeof node === "string") return node; - if (typeof node === "number") return String(node); - if (!node) return ""; +// Helper to extract text from React nodes +const extractText = (node: React.ReactNode): string => { + if (typeof node === "string") return node; + if (typeof node === "number") return String(node); + if (!node) return ""; + if (React.isValidElement(node)) { + const props = node.props as any; + if (props?.children) { + return extractText(props.children as React.ReactNode); + } + } + if (Array.isArray(node)) { + return node.map(extractText).join(""); + } + return ""; +}; - if (React.isValidElement(node)) { - if ( - node.props && - typeof node.props === "object" && - "children" in node.props - ) { - return extractText(node.props.children as React.ReactNode); +const CodeBlock = React.memo( + ({ children }: React.HTMLAttributes) => { + const [lightTokens, setLightTokens] = React.useState(null); + const [darkTokens, setDarkTokens] = React.useState(null); + + // Extract code and language from children + const codeElement = children as React.ReactElement<{ + className?: string; + children: React.ReactNode; + }>; + const language = + codeElement.props.className?.replace(/language-/, "") || ""; + const codeText = extractText(codeElement.props.children); + + React.useEffect(() => { + async function highlight() { + try { + const [light, dark] = await Promise.all([ + codeToTokens(codeText, { + lang: language as BundledLanguage, + theme: "github-light", + }), + codeToTokens(codeText, { + lang: language as BundledLanguage, + theme: "github-dark", + }), + ]); + setLightTokens(light); + setDarkTokens(dark); + } catch (error) { + console.error("Failed to highlight code:", error); } } - - if (Array.isArray(node)) { - return node.map(extractText).join(""); - } - - return ""; - }, []); - - const language = className?.replace(/language-/, "") || ""; + highlight(); + }, [codeText, language]); return (
-
-
- {language} -
+
+ {language && ( +
+ {language} +
+ )}
-
-          {children}
+        {/* Light mode */}
+        
+          
+            {lightTokens
+              ? lightTokens.tokens.map((line: any, i: number) => (
+                  
+                    {line.map((token: any, j: number) => (
+                      
+                        {token.content}
+                      
+                    ))}
+                    {i < lightTokens.tokens.length - 1 && "\n"}
+                  
+                ))
+              : codeText}
+          
+        
+ {/* Dark mode */} +
+          
+            {darkTokens
+              ? darkTokens.tokens.map((line: any, i: number) => (
+                  
+                    {line.map((token: any, j: number) => (
+                      
+                        {token.content}
+                      
+                    ))}
+                    {i < darkTokens.tokens.length - 1 && "\n"}
+                  
+                ))
+              : codeText}
+          
         
); @@ -68,65 +128,19 @@ const CodeBlock = React.memo( ); const StreamingMarkdownContent: React.FC = - React.memo( - ({ content, isStreaming = false, size, onLastNode, browserToolResult }) => { - // Build the remark plugins array - const remarkPlugins = React.useMemo(() => { - const plugins: PluggableList = [ - remarkGfm, - [remarkMath, { singleDollarTextMath: false }], - remarkCitationParser, - ]; + React.memo(({ content, isStreaming = false, size, browserToolResult }) => { + // Build the remark plugins array - keep default GFM and Math, add citations + const remarkPlugins = React.useMemo(() => { + return [ + defaultRemarkPlugins.gfm, + defaultRemarkPlugins.math, + remarkCitationParser, + ]; + }, []); - // Add streaming plugin when in streaming mode - if (isStreaming) { - plugins.push([remarkStreamingMarkdown, { debug: true, onLastNode }]); - } - - return plugins; - }, [isStreaming, onLastNode]); - - // Create a custom sanitization schema that allows math elements - const sanitizeSchema = React.useMemo(() => { - return { - ...defaultSchema, - attributes: { - ...defaultSchema.attributes, - span: [ - ...(defaultSchema.attributes?.span || []), - ["className", /^katex/], - ], - div: [ - ...(defaultSchema.attributes?.div || []), - ["className", /^katex/], - ], - "ol-citation": ["cursor", "start", "end"], - }, - tagNames: [ - ...(defaultSchema.tagNames || []), - "math", - "mrow", - "mi", - "mo", - "mn", - "msup", - "msub", - "mfrac", - "mover", - "munder", - "msqrt", - "mroot", - "merror", - "mspace", - "mpadded", - "ol-citation", - ], - }; - }, []); - - return ( -
= prose-pre:my-0 prose-pre:max-w-full prose-pre:pt-1 - [&_code:not(pre_code)]:text-neutral-700 + [&_code:not(pre_code)]:text-neutral-700 [&_code:not(pre_code)]:bg-neutral-100 [&_code:not(pre_code)]:font-normal [&_code:not(pre_code)]:px-1.5 @@ -167,104 +181,115 @@ const StreamingMarkdownContent: React.FC = dark:prose-li:marker:text-neutral-300 break-words `} + > + - - ) => ( -
- {children}
-
- ), - // @ts-expect-error: custom type - "ol-citation": ({ - cursor, - // start, - // end, - }: { - cursor: number; - start: number; - end: number; - }) => { - // Check if we have a page_stack and if the cursor is valid - const pageStack = browserToolResult?.page_stack; - const hasValidPage = pageStack && cursor < pageStack.length; - const pageUrl = hasValidPage ? pageStack[cursor] : null; + ) => ( +
+ + {children} +
+
+ ), + thead: ({ + children, + ...props + }: React.HTMLAttributes) => ( + + {children} + + ), + th: ({ + children, + ...props + }: React.HTMLAttributes) => ( + + {children} + + ), + td: ({ + children, + ...props + }: React.HTMLAttributes) => ( + + {children} + + ), + // @ts-expect-error: custom citation type + "ol-citation": ({ + cursor, + }: { + cursor: number; + start: number; + end: number; + }) => { + const pageStack = browserToolResult?.page_stack; + const hasValidPage = pageStack && cursor < pageStack.length; + const pageUrl = hasValidPage ? pageStack[cursor] : null; - // Extract a readable title from the URL if possible - const getPageTitle = (url: string) => { - if (url.startsWith("search_results_")) { - const searchTerm = url.substring( - "search_results_".length, - ); - return `Search: ${searchTerm}`; - } - // For regular URLs, try to extract domain or use full URL - try { - const urlObj = new URL(url); - return urlObj.hostname; - } catch { - // If not a valid URL, return as is - return url; - } - }; - - const citationElement = ( - - [{cursor}] - - ); - - // If we have a valid page URL, wrap in a link - if (pageUrl && pageUrl.startsWith("http")) { - return ( - - {citationElement} - - ); + const getPageTitle = (url: string) => { + if (url.startsWith("search_results_")) { + const searchTerm = url.substring("search_results_".length); + return `Search: ${searchTerm}`; } + try { + const urlObj = new URL(url); + return urlObj.hostname; + } catch { + return url; + } + }; - // Otherwise, just return the citation without a link - return citationElement; - }, - }} - > - {content} -
-
-
- ); - }, - ); + const citationElement = ( + + [{cursor}] + + ); + + if (pageUrl && pageUrl.startsWith("http")) { + return ( + + {citationElement} + + ); + } + + return citationElement; + }, + }} + > + {content} + + +
+ ); + }); interface StreamingMarkdownErrorBoundaryProps { content: string; diff --git a/app/ui/app/src/index.css b/app/ui/app/src/index.css index 0493d51c..af1f265d 100644 --- a/app/ui/app/src/index.css +++ b/app/ui/app/src/index.css @@ -16,793 +16,6 @@ --text-color: #ffffff; } } -@media (prefers-color-scheme: light) { - .prose { - /** - * One Light theme for prism.js - * Based on Atom's One Light theme: https://github.com/atom/atom/tree/master/packages/one-light-syntax - */ - - /** - * One Light colours (accurate as of commit eb064bf on 19 Feb 2021) - * From colors.less - * --mono-1: hsl(230, 8%, 24%); - * --mono-2: hsl(230, 6%, 44%); - * --mono-3: hsl(230, 4%, 64%) - * --hue-1: hsl(198, 99%, 37%); - * --hue-2: hsl(221, 87%, 60%); - * --hue-3: hsl(301, 63%, 40%); - * --hue-4: hsl(119, 34%, 47%); - * --hue-5: hsl(5, 74%, 59%); - * --hue-5-2: hsl(344, 84%, 43%); - * --hue-6: hsl(35, 99%, 36%); - * --hue-6-2: hsl(35, 99%, 40%); - * --syntax-fg: hsl(230, 8%, 24%); - * --syntax-bg: hsl(230, 1%, 98%); - * --syntax-gutter: hsl(230, 1%, 62%); - * --syntax-guide: hsla(230, 8%, 24%, 0.2); - * --syntax-accent: hsl(230, 100%, 66%); - * From syntax-variables.less - * --syntax-selection-color: hsl(230, 1%, 90%); - * --syntax-gutter-background-color-selected: hsl(230, 1%, 90%); - * --syntax-cursor-line: hsla(230, 8%, 24%, 0.05); - */ - - .token.comment, - .token.prolog, - .token.cdata { - color: hsl(230, 4%, 64%); - } - - .token.doctype, - .token.punctuation, - .token.entity { - color: hsl(230, 8%, 24%); - } - - .token.attr-name, - .token.class-name, - .token.boolean, - .token.constant, - .token.number, - .token.atrule { - color: hsl(35, 99%, 36%); - } - - .token.keyword { - color: hsl(301, 63%, 40%); - } - - .token.property, - .token.tag, - .token.symbol, - .token.deleted, - .token.important { - color: hsl(5, 74%, 59%); - } - - .token.selector, - .token.string, - .token.char, - .token.builtin, - .token.inserted, - .token.regex, - .token.attr-value, - .token.attr-value > .token.punctuation { - color: hsl(119, 34%, 47%); - } - - .token.variable, - .token.operator, - .token.function { - color: hsl(221, 87%, 60%); - } - - .token.url { - color: hsl(198, 99%, 37%); - } - - /* HTML overrides */ - .token.attr-value > .token.punctuation.attr-equals, - .token.special-attr > .token.attr-value > .token.value.css { - color: hsl(230, 8%, 24%); - } - - /* CSS overrides */ - .language-css .token.selector { - color: hsl(5, 74%, 59%); - } - - .language-css .token.property { - color: hsl(230, 8%, 24%); - } - - .language-css .token.function, - .language-css .token.url > .token.function { - color: hsl(198, 99%, 37%); - } - - .language-css .token.url > .token.string.url { - color: hsl(119, 34%, 47%); - } - - .language-css .token.important, - .language-css .token.atrule .token.rule { - color: hsl(301, 63%, 40%); - } - - /* JS overrides */ - .language-javascript .token.operator { - color: hsl(301, 63%, 40%); - } - - .language-javascript - .token.template-string - > .token.interpolation - > .token.interpolation-punctuation.punctuation { - color: hsl(344, 84%, 43%); - } - - /* JSON overrides */ - .language-json .token.operator { - color: hsl(230, 8%, 24%); - } - - .language-json .token.null.keyword { - color: hsl(35, 99%, 36%); - } - - /* MD overrides */ - .language-markdown .token.url, - .language-markdown .token.url > .token.operator, - .language-markdown .token.url-reference.url > .token.string { - color: hsl(230, 8%, 24%); - } - - .language-markdown .token.url > .token.content { - color: hsl(221, 87%, 60%); - } - - .language-markdown .token.url > .token.url, - .language-markdown .token.url-reference.url { - color: hsl(198, 99%, 37%); - } - - .language-markdown .token.blockquote.punctuation, - .language-markdown .token.hr.punctuation { - color: hsl(230, 4%, 64%); - font-style: italic; - } - - .language-markdown .token.code-snippet { - color: hsl(119, 34%, 47%); - } - - .language-markdown .token.bold .token.content { - color: hsl(35, 99%, 36%); - } - - .language-markdown .token.italic .token.content { - color: hsl(301, 63%, 40%); - } - - .language-markdown .token.strike .token.content, - .language-markdown .token.strike .token.punctuation, - .language-markdown .token.list.punctuation, - .language-markdown .token.title.important > .token.punctuation { - color: hsl(5, 74%, 59%); - } - - /* General */ - .token.bold { - font-weight: bold; - } - - .token.comment, - .token.italic { - font-style: italic; - } - - .token.entity { - cursor: help; - } - - .token.namespace { - opacity: 0.8; - } - - /* Plugin overrides */ - /* Selectors should have higher specificity than those in the plugins' default stylesheets */ - - /* Show Invisibles plugin overrides */ - .token.token.tab:not(:empty):before, - .token.token.cr:before, - .token.token.lf:before, - .token.token.space:before { - color: hsla(230, 8%, 24%, 0.2); - } - - /* Toolbar plugin overrides */ - /* Space out all buttons and move them away from the right edge of the code block */ - div.code-toolbar > .toolbar.toolbar > .toolbar-item { - margin-right: 0.4em; - } - - /* Styling the buttons */ - div.code-toolbar > .toolbar.toolbar > .toolbar-item > button, - div.code-toolbar > .toolbar.toolbar > .toolbar-item > a, - div.code-toolbar > .toolbar.toolbar > .toolbar-item > span { - background: hsl(230, 1%, 90%); - color: hsl(230, 6%, 44%); - padding: 0.1em 0.4em; - border-radius: 0.3em; - } - - div.code-toolbar > .toolbar.toolbar > .toolbar-item > button:hover, - div.code-toolbar > .toolbar.toolbar > .toolbar-item > button:focus, - div.code-toolbar > .toolbar.toolbar > .toolbar-item > a:hover, - div.code-toolbar > .toolbar.toolbar > .toolbar-item > a:focus, - div.code-toolbar > .toolbar.toolbar > .toolbar-item > span:hover, - div.code-toolbar > .toolbar.toolbar > .toolbar-item > span:focus { - background: hsl(230, 1%, 78%); /* custom: darken(--syntax-bg, 20%) */ - color: hsl(230, 8%, 24%); - } - - /* Line Highlight plugin overrides */ - /* The highlighted line itself */ - .line-highlight.line-highlight { - background: hsla(230, 8%, 24%, 0.05); - } - - /* Default line numbers in Line Highlight plugin */ - .line-highlight.line-highlight:before, - .line-highlight.line-highlight[data-end]:after { - background: hsl(230, 1%, 90%); - color: hsl(230, 8%, 24%); - padding: 0.1em 0.6em; - border-radius: 0.3em; - box-shadow: 0 2px 0 0 rgba(0, 0, 0, 0.2); /* same as Toolbar plugin default */ - } - - /* Hovering over a linkable line number (in the gutter area) */ - /* Requires Line Numbers plugin as well */ - pre[id].linkable-line-numbers.linkable-line-numbers - span.line-numbers-rows - > span:hover:before { - background-color: hsla(230, 8%, 24%, 0.05); - } - - /* Line Numbers and Command Line plugins overrides */ - /* Line separating gutter from coding area */ - .line-numbers.line-numbers .line-numbers-rows, - .command-line .command-line-prompt { - border-right-color: hsla(230, 8%, 24%, 0.2); - } - - /* Stuff in the gutter */ - .line-numbers .line-numbers-rows > span:before, - .command-line .command-line-prompt > span:before { - color: hsl(230, 1%, 62%); - } - - /* Match Braces plugin overrides */ - /* Note: Outline colour is inherited from the braces */ - .rainbow-braces .token.token.punctuation.brace-level-1, - .rainbow-braces .token.token.punctuation.brace-level-5, - .rainbow-braces .token.token.punctuation.brace-level-9 { - color: hsl(5, 74%, 59%); - } - - .rainbow-braces .token.token.punctuation.brace-level-2, - .rainbow-braces .token.token.punctuation.brace-level-6, - .rainbow-braces .token.token.punctuation.brace-level-10 { - color: hsl(119, 34%, 47%); - } - - .rainbow-braces .token.token.punctuation.brace-level-3, - .rainbow-braces .token.token.punctuation.brace-level-7, - .rainbow-braces .token.token.punctuation.brace-level-11 { - color: hsl(221, 87%, 60%); - } - - .rainbow-braces .token.token.punctuation.brace-level-4, - .rainbow-braces .token.token.punctuation.brace-level-8, - .rainbow-braces .token.token.punctuation.brace-level-12 { - color: hsl(301, 63%, 40%); - } - - /* Diff Highlight plugin overrides */ - /* Taken from https://github.com/atom/github/blob/master/styles/variables.less */ - pre.diff-highlight > code .token.token.deleted:not(.prefix), - pre > code.diff-highlight .token.token.deleted:not(.prefix) { - background-color: hsla(353, 100%, 66%, 0.15); - } - - pre.diff-highlight > code .token.token.deleted:not(.prefix)::-moz-selection, - pre.diff-highlight - > code - .token.token.deleted:not(.prefix) - *::-moz-selection, - pre > code.diff-highlight .token.token.deleted:not(.prefix)::-moz-selection, - pre - > code.diff-highlight - .token.token.deleted:not(.prefix) - *::-moz-selection { - background-color: hsla(353, 95%, 66%, 0.25); - } - - pre.diff-highlight > code .token.token.deleted:not(.prefix)::selection, - pre.diff-highlight > code .token.token.deleted:not(.prefix) *::selection, - pre > code.diff-highlight .token.token.deleted:not(.prefix)::selection, - pre > code.diff-highlight .token.token.deleted:not(.prefix) *::selection { - background-color: hsla(353, 95%, 66%, 0.25); - } - - pre.diff-highlight > code .token.token.inserted:not(.prefix), - pre > code.diff-highlight .token.token.inserted:not(.prefix) { - background-color: hsla(137, 100%, 55%, 0.15); - } - - pre.diff-highlight - > code - .token.token.inserted:not(.prefix)::-moz-selection, - pre.diff-highlight - > code - .token.token.inserted:not(.prefix) - *::-moz-selection, - pre - > code.diff-highlight - .token.token.inserted:not(.prefix)::-moz-selection, - pre - > code.diff-highlight - .token.token.inserted:not(.prefix) - *::-moz-selection { - background-color: hsla(135, 73%, 55%, 0.25); - } - - pre.diff-highlight > code .token.token.inserted:not(.prefix)::selection, - pre.diff-highlight > code .token.token.inserted:not(.prefix) *::selection, - pre > code.diff-highlight .token.token.inserted:not(.prefix)::selection, - pre > code.diff-highlight .token.token.inserted:not(.prefix) *::selection { - background-color: hsla(135, 73%, 55%, 0.25); - } - - /* Previewers plugin overrides */ - /* Based on https://github.com/atom-community/atom-ide-datatip/blob/master/styles/atom-ide-datatips.less and https://github.com/atom/atom/blob/master/packages/one-light-ui */ - /* Border around popup */ - .prism-previewer.prism-previewer:before, - .prism-previewer-gradient.prism-previewer-gradient div { - border-color: hsl(0, 0, 95%); - } - - /* Angle and time should remain as circles and are hence not included */ - .prism-previewer-color.prism-previewer-color:before, - .prism-previewer-gradient.prism-previewer-gradient div, - .prism-previewer-easing.prism-previewer-easing:before { - border-radius: 0.3em; - } - - /* Triangles pointing to the code */ - .prism-previewer.prism-previewer:after { - border-top-color: hsl(0, 0, 95%); - } - - .prism-previewer-flipped.prism-previewer-flipped.after { - border-bottom-color: hsl(0, 0, 95%); - } - - /* Background colour within the popup */ - .prism-previewer-angle.prism-previewer-angle:before, - .prism-previewer-time.prism-previewer-time:before, - .prism-previewer-easing.prism-previewer-easing { - background: hsl(0, 0%, 100%); - } - - /* For angle, this is the positive area (eg. 90deg will display one quadrant in this colour) */ - /* For time, this is the alternate colour */ - .prism-previewer-angle.prism-previewer-angle circle, - .prism-previewer-time.prism-previewer-time circle { - stroke: hsl(230, 8%, 24%); - stroke-opacity: 1; - } - - /* Stroke colours of the handle, direction point, and vector itself */ - .prism-previewer-easing.prism-previewer-easing circle, - .prism-previewer-easing.prism-previewer-easing path, - .prism-previewer-easing.prism-previewer-easing line { - stroke: hsl(230, 8%, 24%); - } - - /* Fill colour of the handle */ - .prism-previewer-easing.prism-previewer-easing circle { - fill: transparent; - } - } -} - -@media (prefers-color-scheme: dark) { - .prose { - .token.comment, - .token.prolog, - .token.cdata { - color: hsl(220, 10%, 40%); - } - - .token.doctype, - .token.punctuation, - .token.entity { - color: hsl(220, 14%, 71%); - } - - .token.attr-name, - .token.class-name, - .token.boolean, - .token.constant, - .token.number, - .token.atrule { - color: hsl(29, 54%, 61%); - } - - .token.keyword { - color: hsl(286, 60%, 67%); - } - - .token.property, - .token.tag, - .token.symbol, - .token.deleted, - .token.important { - color: hsl(355, 65%, 65%); - } - - .token.selector, - .token.string, - .token.char, - .token.builtin, - .token.inserted, - .token.regex, - .token.attr-value, - .token.attr-value > .token.punctuation { - color: hsl(95, 38%, 62%); - } - - .token.variable, - .token.operator, - .token.function { - color: hsl(207, 82%, 66%); - } - - .token.url { - color: hsl(187, 47%, 55%); - } - - /* HTML overrides */ - .token.attr-value > .token.punctuation.attr-equals, - .token.special-attr > .token.attr-value > .token.value.css { - color: hsl(220, 14%, 71%); - } - - /* CSS overrides */ - .language-css .token.selector { - color: hsl(355, 65%, 65%); - } - - .language-css .token.property { - color: hsl(220, 14%, 71%); - } - - .language-css .token.function, - .language-css .token.url > .token.function { - color: hsl(187, 47%, 55%); - } - - .language-css .token.url > .token.string.url { - color: hsl(95, 38%, 62%); - } - - .language-css .token.important, - .language-css .token.atrule .token.rule { - color: hsl(286, 60%, 67%); - } - - /* JS overrides */ - .language-javascript .token.operator { - color: hsl(286, 60%, 67%); - } - - .language-javascript - .token.template-string - > .token.interpolation - > .token.interpolation-punctuation.punctuation { - color: hsl(5, 48%, 51%); - } - - /* JSON overrides */ - .language-json .token.operator { - color: hsl(220, 14%, 71%); - } - - .language-json .token.null.keyword { - color: hsl(29, 54%, 61%); - } - - /* MD overrides */ - .language-markdown .token.url, - .language-markdown .token.url > .token.operator, - .language-markdown .token.url-reference.url > .token.string { - color: hsl(220, 14%, 71%); - } - - .language-markdown .token.url > .token.content { - color: hsl(207, 82%, 66%); - } - - .language-markdown .token.url > .token.url, - .language-markdown .token.url-reference.url { - color: hsl(187, 47%, 55%); - } - - .language-markdown .token.blockquote.punctuation, - .language-markdown .token.hr.punctuation { - color: hsl(220, 10%, 40%); - font-style: italic; - } - - .language-markdown .token.code-snippet { - color: hsl(95, 38%, 62%); - } - - .language-markdown .token.bold .token.content { - color: hsl(29, 54%, 61%); - } - - .language-markdown .token.italic .token.content { - color: hsl(286, 60%, 67%); - } - - .language-markdown .token.strike .token.content, - .language-markdown .token.strike .token.punctuation, - .language-markdown .token.list.punctuation, - .language-markdown .token.title.important > .token.punctuation { - color: hsl(355, 65%, 65%); - } - - /* General */ - .token.bold { - font-weight: bold; - } - - .token.comment, - .token.italic { - font-style: italic; - } - - .token.entity { - cursor: help; - } - - .token.namespace { - opacity: 0.8; - } - - /* Plugin overrides */ - /* Selectors should have higher specificity than those in the plugins' default stylesheets */ - - /* Show Invisibles plugin overrides */ - .token.token.tab:not(:empty):before, - .token.token.cr:before, - .token.token.lf:before, - .token.token.space:before { - color: hsla(220, 14%, 71%, 0.15); - text-shadow: none; - } - - /* Toolbar plugin overrides */ - /* Space out all buttons and move them away from the right edge of the code block */ - div.code-toolbar > .toolbar.toolbar > .toolbar-item { - margin-right: 0.4em; - } - - /* Styling the buttons */ - div.code-toolbar > .toolbar.toolbar > .toolbar-item > button, - div.code-toolbar > .toolbar.toolbar > .toolbar-item > a, - div.code-toolbar > .toolbar.toolbar > .toolbar-item > span { - background: hsl(220, 13%, 26%); - color: hsl(220, 9%, 55%); - padding: 0.1em 0.4em; - border-radius: 0.3em; - } - - div.code-toolbar > .toolbar.toolbar > .toolbar-item > button:hover, - div.code-toolbar > .toolbar.toolbar > .toolbar-item > button:focus, - div.code-toolbar > .toolbar.toolbar > .toolbar-item > a:hover, - div.code-toolbar > .toolbar.toolbar > .toolbar-item > a:focus, - div.code-toolbar > .toolbar.toolbar > .toolbar-item > span:hover, - div.code-toolbar > .toolbar.toolbar > .toolbar-item > span:focus { - background: hsl(220, 13%, 28%); - color: hsl(220, 14%, 71%); - } - - /* Line Highlight plugin overrides */ - /* The highlighted line itself */ - .line-highlight.line-highlight { - background: hsla(220, 100%, 80%, 0.04); - } - - /* Default line numbers in Line Highlight plugin */ - .line-highlight.line-highlight:before, - .line-highlight.line-highlight[data-end]:after { - background: hsl(220, 13%, 26%); - color: hsl(220, 14%, 71%); - padding: 0.1em 0.6em; - border-radius: 0.3em; - box-shadow: 0 2px 0 0 rgba(0, 0, 0, 0.2); /* same as Toolbar plugin default */ - } - - /* Hovering over a linkable line number (in the gutter area) */ - /* Requires Line Numbers plugin as well */ - pre[id].linkable-line-numbers.linkable-line-numbers - span.line-numbers-rows - > span:hover:before { - background-color: hsla(220, 100%, 80%, 0.04); - } - - /* Line Numbers and Command Line plugins overrides */ - /* Line separating gutter from coding area */ - .line-numbers.line-numbers .line-numbers-rows, - .command-line .command-line-prompt { - border-right-color: hsla(220, 14%, 71%, 0.15); - } - - /* Stuff in the gutter */ - .line-numbers .line-numbers-rows > span:before, - .command-line .command-line-prompt > span:before { - color: hsl(220, 14%, 45%); - } - - /* Match Braces plugin overrides */ - /* Note: Outline colour is inherited from the braces */ - .rainbow-braces .token.token.punctuation.brace-level-1, - .rainbow-braces .token.token.punctuation.brace-level-5, - .rainbow-braces .token.token.punctuation.brace-level-9 { - color: hsl(355, 65%, 65%); - } - - .rainbow-braces .token.token.punctuation.brace-level-2, - .rainbow-braces .token.token.punctuation.brace-level-6, - .rainbow-braces .token.token.punctuation.brace-level-10 { - color: hsl(95, 38%, 62%); - } - - .rainbow-braces .token.token.punctuation.brace-level-3, - .rainbow-braces .token.token.punctuation.brace-level-7, - .rainbow-braces .token.token.punctuation.brace-level-11 { - color: hsl(207, 82%, 66%); - } - - .rainbow-braces .token.token.punctuation.brace-level-4, - .rainbow-braces .token.token.punctuation.brace-level-8, - .rainbow-braces .token.token.punctuation.brace-level-12 { - color: hsl(286, 60%, 67%); - } - - /* Diff Highlight plugin overrides */ - /* Taken from https://github.com/atom/github/blob/master/styles/variables.less */ - pre.diff-highlight > code .token.token.deleted:not(.prefix), - pre > code.diff-highlight .token.token.deleted:not(.prefix) { - background-color: hsla(353, 100%, 66%, 0.15); - } - - pre.diff-highlight > code .token.token.deleted:not(.prefix)::-moz-selection, - pre.diff-highlight - > code - .token.token.deleted:not(.prefix) - *::-moz-selection, - pre > code.diff-highlight .token.token.deleted:not(.prefix)::-moz-selection, - pre - > code.diff-highlight - .token.token.deleted:not(.prefix) - *::-moz-selection { - background-color: hsla(353, 95%, 66%, 0.25); - } - - pre.diff-highlight > code .token.token.deleted:not(.prefix)::selection, - pre.diff-highlight > code .token.token.deleted:not(.prefix) *::selection, - pre > code.diff-highlight .token.token.deleted:not(.prefix)::selection, - pre > code.diff-highlight .token.token.deleted:not(.prefix) *::selection { - background-color: hsla(353, 95%, 66%, 0.25); - } - - pre.diff-highlight > code .token.token.inserted:not(.prefix), - pre > code.diff-highlight .token.token.inserted:not(.prefix) { - background-color: hsla(137, 100%, 55%, 0.15); - } - - pre.diff-highlight - > code - .token.token.inserted:not(.prefix)::-moz-selection, - pre.diff-highlight - > code - .token.token.inserted:not(.prefix) - *::-moz-selection, - pre - > code.diff-highlight - .token.token.inserted:not(.prefix)::-moz-selection, - pre - > code.diff-highlight - .token.token.inserted:not(.prefix) - *::-moz-selection { - background-color: hsla(135, 73%, 55%, 0.25); - } - - pre.diff-highlight > code .token.token.inserted:not(.prefix)::selection, - pre.diff-highlight > code .token.token.inserted:not(.prefix) *::selection, - pre > code.diff-highlight .token.token.inserted:not(.prefix)::selection, - pre > code.diff-highlight .token.token.inserted:not(.prefix) *::selection { - background-color: hsla(135, 73%, 55%, 0.25); - } - - /* Previewers plugin overrides */ - /* Based on https://github.com/atom-community/atom-ide-datatip/blob/master/styles/atom-ide-datatips.less and https://github.com/atom/atom/blob/master/packages/one-dark-ui */ - /* Border around popup */ - .prism-previewer.prism-previewer:before, - .prism-previewer-gradient.prism-previewer-gradient div { - border-color: hsl(224, 13%, 17%); - } - - /* Angle and time should remain as circles and are hence not included */ - .prism-previewer-color.prism-previewer-color:before, - .prism-previewer-gradient.prism-previewer-gradient div, - .prism-previewer-easing.prism-previewer-easing:before { - border-radius: 0.3em; - } - - /* Triangles pointing to the code */ - .prism-previewer.prism-previewer:after { - border-top-color: hsl(224, 13%, 17%); - } - - .prism-previewer-flipped.prism-previewer-flipped.after { - border-bottom-color: hsl(224, 13%, 17%); - } - - /* Background colour within the popup */ - .prism-previewer-angle.prism-previewer-angle:before, - .prism-previewer-time.prism-previewer-time:before, - .prism-previewer-easing.prism-previewer-easing { - background: hsl(219, 13%, 22%); - } - - /* For angle, this is the positive area (eg. 90deg will display one quadrant in this colour) */ - /* For time, this is the alternate colour */ - .prism-previewer-angle.prism-previewer-angle circle, - .prism-previewer-time.prism-previewer-time circle { - stroke: hsl(220, 14%, 71%); - stroke-opacity: 1; - } - - /* Stroke colours of the handle, direction point, and vector itself */ - .prism-previewer-easing.prism-previewer-easing circle, - .prism-previewer-easing.prism-previewer-easing path, - .prism-previewer-easing.prism-previewer-easing line { - stroke: hsl(220, 14%, 71%); - } - - /* Fill colour of the handle */ - .prism-previewer-easing.prism-previewer-easing circle { - fill: transparent; - } - } -} - -.prose pre { - contain: layout style; -} - -/* Or more aggressively */ -.prose pre code { - contain: layout style paint; -} /* messaging-style typing indicator animation */ @keyframes typing { diff --git a/app/ui/app/src/utils/processStreamingMarkdown.ts b/app/ui/app/src/utils/processStreamingMarkdown.ts deleted file mode 100644 index faab9b5c..00000000 --- a/app/ui/app/src/utils/processStreamingMarkdown.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { remark } from "remark"; -import remarkStringify from "remark-stringify"; -import remarkStreamingMarkdown from "./remarkStreamingMarkdown"; - -/** - * Process markdown content for streaming display using the remark plugin. - * This is primarily used for testing the remark plugin with string inputs/outputs. - */ -export function processStreamingMarkdown(content: string): string { - if (!content) return content; - - const result = remark() - .use(remarkStreamingMarkdown, { debug: false }) - .use(remarkStringify) - .processSync(content); - - // remove trailing newline to keep tests cleaner - let output = result.toString(); - if (output.endsWith("\n")) { - output = output.slice(0, -1); - } - - return output; -} diff --git a/app/ui/app/src/utils/remarkStreamingMarkdown.ts b/app/ui/app/src/utils/remarkStreamingMarkdown.ts deleted file mode 100644 index 6b6d4cd7..00000000 --- a/app/ui/app/src/utils/remarkStreamingMarkdown.ts +++ /dev/null @@ -1,447 +0,0 @@ -import { parents, type Proxy } from "unist-util-parents"; -import type { Plugin } from "unified"; -import type { - Emphasis, - Node, - Parent, - Root, - RootContent, - Text, - Strong, - PhrasingContent, - Paragraph, -} from "mdast"; -import { u } from "unist-builder"; - -declare module "unist" { - interface Node { - /** Added by `unist-util-parents` (or your own walk). */ - parent?: Proxy & Parent; - } -} - -// interface SimpleTextRule { -// pattern: RegExp; -// transform: (matches: RegExpExecArray[], lastNode: Proxy) => void; -// } - -// const simpleTextRules: SimpleTextRule[] = [ -// // TODO(drifkin): generalize this for `__`/`_`/`~~`/`~` etc. -// { -// pattern: /(\*\*)(?=\S|$)/g, -// transform: (matchesIterator, lastNode) => { -// const textNode = lastNode.node as Text; - -// const matches = [...matchesIterator]; -// const lastMatch = matches[matches.length - 1]; -// const origValue = textNode.value; -// const start = lastMatch.index; -// const sep = lastMatch[1]; - -// const before = origValue.slice(0, start); -// const after = origValue.slice(start + sep.length); - -// if (lastNode.parent) { -// const index = (lastNode.parent.node as Parent).children.indexOf( -// lastNode.node as RootContent, -// ); -// const shouldRemove = before.length === 0; -// if (!shouldRemove) { -// textNode.value = before; -// } - -// const newNode = u("strong", { -// children: [u("text", { value: after })], -// }); -// (lastNode.parent.node as Parent).children.splice( -// index + (shouldRemove ? 0 : 1), -// shouldRemove ? 1 : 0, -// newNode, -// ); -// } -// }, -// }, -// ]; - -interface Options { - debug?: boolean; - onLastNode?: (info: LastNodeInfo) => void; -} - -export interface LastNodeInfo { - path: string[]; - type: string; - value?: string; - lastChars?: string; - fullNode: Node; -} - -/** - * Removes `child` from `parent` in-place. - * @returns `true` if the child was found and removed; `false` otherwise. - */ -export function removeChildFromParent( - child: RootContent, - parent: Node, -): boolean { - if (!isParent(parent)) return false; // parent isn’t a Parent → nothing to do - - const idx = parent.children.indexOf(child); - if (idx < 0) return false; // not a child → nothing to remove - - parent.children.splice(idx, 1); - return true; // removal successful -} - -/** Narrow a generic `Node` to a `Parent` (i.e. one that really has children). */ -function isParent(node: Node): node is Parent { - // A `Parent` always has a `children` array; make sure it's an array first. - return Array.isArray((node as Partial).children); -} - -/** - * Follow “last-child” pointers until you reach a leaf. - * Returns the right-most, deepest node in source order. - */ -export function findRightmostDeepestNode(root: Node): Node { - let current: Node = root; - - // While the current node *is* a Parent and has at least one child… - while (isParent(current) && current.children.length > 0) { - const lastIndex = current.children.length - 1; - current = current.children[lastIndex]; - } - - return current; // Leaf: no further children -} - -const remarkStreamingMarkdown: Plugin<[Options?], Root> = () => { - return (tree) => { - const treeWithParents = parents(tree); - const lastNode = findRightmostDeepestNode(treeWithParents) as Proxy; - - const parentNode = lastNode.parent; - const grandparentNode = parentNode?.parent; - - let ruleMatched = false; - - // handling `* *` -> `` - // - // if the last node is part of a -> - // -> , then we need to - // remove everything up to and including the first list item. This happens - // when we have `* *`, which can become a bolded list item OR a horizontal - // line - if ( - lastNode.type === "listItem" && - parentNode && - grandparentNode && - parentNode.type === "list" && - grandparentNode.type === "listItem" && - parentNode.children.length === 1 && - grandparentNode.children.length === 1 - ) { - ruleMatched = true; - if (grandparentNode.parent) { - removeChildFromParent( - grandparentNode.node as RootContent, - grandparentNode.parent.node, - ); - } - // Handle `*` -> ``: - // - // if the last node is just an empty list item, we need to remove it - // because it could become something else (e.g., a horizontal line) - } else if ( - lastNode.type === "listItem" && - parentNode && - parentNode.type === "list" - ) { - ruleMatched = true; - removeChildFromParent(lastNode.node as RootContent, parentNode.node); - } else if (lastNode.type === "thematicBreak") { - ruleMatched = true; - const parent = lastNode.parent; - if (parent) { - removeChildFromParent(lastNode.node as RootContent, parent.node); - } - } else if (lastNode.type === "text") { - const textNode = lastNode.node as Text; - if (textNode.value.endsWith("**")) { - ruleMatched = true; - textNode.value = textNode.value.slice(0, -2); - // if there's a newline then a number, this is very very likely a - // numbered list item. Let's just hide it until the period comes (or - // other text disambiguates it) - } else { - const match = textNode.value.match(/^([0-9]+)$/m); - if (match) { - const number = match[1]; - textNode.value = textNode.value.slice(0, -number.length - 1); - ruleMatched = true; - // if the text node is now empty, then we might want to remove other - // elements, like a now-empty containing paragraph, or a break that - // might disappear once more tokens come in - if (textNode.value.length === 0) { - if ( - lastNode.parent?.type === "paragraph" && - lastNode.parent.children.length === 1 - ) { - // remove the whole paragraph if it's now empty (otherwise it'll - // cause an extra newline that might not last) - removeChildFromParent( - lastNode.parent.node as Paragraph, - lastNode.parent.parent?.node as Node, - ); - } else { - const prev = prevSibling(lastNode); - if (prev?.type === "break") { - removeChildFromParent( - prev.node as RootContent, - lastNode.parent?.node as Node, - ); - removeChildFromParent( - lastNode.node as RootContent, - lastNode.parent?.node as Node, - ); - } - } - } - } - } - } - - if (ruleMatched) { - return tree; - } - - // we need to - // a case like - // - *def `abc` [abc **def**](abc)* - // is pretty tricky, because if we land just after def, then we actually - // have two separate tags to process at two different parents. Maybe we - // need to keep iterating up until we find a paragraph, but process each - // parent on the way up. Hmm, well actually after `def` we won't even be a proper link yet - // TODO(drifkin): it's really if the last node's parent is a paragraph, for which the following is a sub-cas where the lastNode is a text node. - // And instead of just processing simple text rules, they need to operate on the whole paragraph - // like `**[abc](def)` needs to become `**[abc](def)**` - - // if we're just text at the end, then we should remove some ambiguous characters - - if (lastNode.parent) { - const didChange = processParent(lastNode.parent as Parent & Proxy); - if (didChange) { - // TODO(drifkin): need to fix up the tree, but not sure lastNode will still exist? Check all the transforms to see if it's safe to find the last node again - // - // need to regen the tree w/ parents since reparenting could've happened - // treeWithParents = parents(tree); - } - } - - const grandparent = lastNode.parent?.parent; - // TODO(drifkin): let's go arbitrarily high up the tree, but limiting it - // to 2 levels for now until I think more about the stop condition - if (grandparent) { - processParent(grandparent as Parent & Proxy); - } - - // console.log("ruleMatched", ruleMatched); - - // } else if (lastNode.parent?.type === "paragraph") { - // console.log("!!! paragraph"); - // console.log("lastNode.parent", lastNode.parent); - - // // Handle `**abc*` -> `**abc**`: - // // We detect this when the last child is an emphasis node, and it's preceded by a text node that ends with `*` - // const paragraph = lastNode.parent as Proxy & Paragraph; - // if (paragraph.children.length >= 2) { - // const lastChild = paragraph.children[paragraph.children.length - 1]; - // if (lastChild.type === "emphasis") { - // const sibling = paragraph.children[paragraph.children.length - 2]; - // if (sibling.type === "text") { - // const siblingText = sibling as Text & Proxy; - // if (siblingText.value.endsWith("*")) { - // ruleMatched = true; - // const textNode = (lastNode as Proxy).node as Text; - // textNode.value = textNode.value.slice(0, -1); - // paragraph.node.type = "strong"; - // } - // } - // } - // } - // } else if (lastNode.type === "text") { - // // Handle `**abc*` -> `**abc**`: - // // - // // this gets parsed as a text node ending in `*` followed by an emphasis - // // node. So if we're in text, we need to check if our parent is emphasis, - // // and then get our parent's sibling before it and check if it ends with - // // `*` - // const parent = lastNode.parent; - // if (parent && parent.type === "emphasis") { - // const grandparent = parent.parent; - // if (grandparent) { - // const index = (grandparent.node as Parent).children.indexOf( - // parent.node as RootContent, - // ); - // if (index > 0) { - // const prevNode = grandparent.children[index - 1]; - // if ( - // prevNode.type === "text" && - // (prevNode as Text).value.endsWith("*") - // ) { - // ruleMatched = true; - // const textNode = (prevNode as Proxy).node as Text; - // textNode.value = textNode.value.slice(0, -1); - // parent.node.type = "strong"; - // } - // } - // } - // } - - // if (!ruleMatched) { - // // if the last node is just text, then we process it in order to fix up certain unclosed items - // // e.g., `**abc` -> `**abc**` - // const textNode = lastNode.node as Text; - // for (const rule of simpleTextRules) { - // const matchesIterator = textNode.value.matchAll(rule.pattern); - // const matches = [...matchesIterator]; - // if (matches.length > 0) { - // rule.transform(matches, lastNode); - // ruleMatched = true; - // break; - // } - // } - // } - // } else if (!ruleMatched) { - // // console.log("no rule matched", lastNode); - // } - - return tree; - }; -}; - -function processParent(parent: Parent & Proxy): boolean { - if (parent.type === "emphasis") { - // Handle `**abc*` -> `**abc**`: - // We detect this when we end with an emphasis node, and it's preceded by - // a text node that ends with `*` - // TODO(drifkin): the last node can be more deeply nested (e.g., a code - // literal in a link), so we probably need to walk up the tree until we - // find an emphasis node or a block? For now we'll just go up one layer to - // catch the most common cases - const emphasisNode = parent as Emphasis & Proxy; - const grandparent = emphasisNode.parent; - if (grandparent) { - const indexOfEmphasisNode = (grandparent.node as Parent).children.indexOf( - emphasisNode.node as RootContent, - ); - if (indexOfEmphasisNode >= 0) { - const nodeBefore = grandparent.children[indexOfEmphasisNode - 1] as - | (Node & Proxy) - | undefined; - if (nodeBefore?.type === "text") { - const textNode = nodeBefore.node as Text; - if (textNode.value.endsWith("*")) { - const strBefore = textNode.value.slice(0, -1); - textNode.value = strBefore; - const strongNode = u("strong", { - children: emphasisNode.children, - }); - (grandparent.node as Parent).children.splice( - indexOfEmphasisNode, - 1, - strongNode, - ); - return true; - } - } - } - } - } - - // Let's check if we have any bold items to close - for (let i = parent.children.length - 1; i >= 0; i--) { - const child = parent.children[i]; - if (child.type === "text") { - const textNode = child as Text & Proxy; - const sep = "**"; - const index = textNode.value.lastIndexOf(sep); - if (index >= 0) { - let isValidOpening = false; - if (index + sep.length < textNode.value.length) { - const charAfter = textNode.value[index + sep.length]; - if (!isWhitespace(charAfter)) { - isValidOpening = true; - } - } else { - if (i < parent.children.length - 1) { - // TODO(drifkin): I'm not sure that this check is strict enough. - // We're trying to detect cases like `**[abc]()` where the char - // after the opening ** is indeed a non-whitespace character. We're - // using the heuristic that there's another item after the current - // one, but I'm not sure if that is good enough. In a well - // constructed tree, there aren't two text nodes in a row, so this - // _seems_ good, but I should think through it more - isValidOpening = true; - } - } - - if (isValidOpening) { - // TODO(drifkin): close the bold - const strBefore = textNode.value.slice(0, index); - const strAfter = textNode.value.slice(index + sep.length); - (textNode.node as Text).value = strBefore; - // TODO(drifkin): the node above could be empty in which case we probably want to delete it - const children: PhrasingContent[] = [ - ...(strAfter.length > 0 ? [u("text", { value: strAfter })] : []), - ]; - const strongNode: Strong = u("strong", { - children, - }); - const nodesAfter = (parent.node as Parent).children.splice( - i + 1, - parent.children.length - i - 1, - strongNode, - ); - // TODO(drifkin): this cast seems iffy, should see if we can cast the - // parent instead, which would also help us check some of our - // assumptions - strongNode.children.push(...(nodesAfter as PhrasingContent[])); - return true; - } - } - } - } - - return false; -} - -function prevSibling(node: Node & Proxy): (Node & Proxy) | null { - const parent = node.parent; - if (parent) { - const index = parent.children.indexOf(node); - return parent.children[index - 1] as Node & Proxy; - } - return null; -} - -function isWhitespace(str: string) { - return str.trim() === ""; -} - -// function debugPrintTreeNoPos(tree: Node) { -// console.log( -// JSON.stringify( -// tree, -// (key, value) => { -// if (key === "position") { -// return undefined; -// } -// return value; -// }, -// 2, -// ), -// ); -// } - -export default remarkStreamingMarkdown; From 74586aa9dfa804fdc7a15e14c045bc9df047e0cd Mon Sep 17 00:00:00 2001 From: Eva Ho Date: Wed, 5 Nov 2025 12:55:14 -0500 Subject: [PATCH 02/32] address comments --- .../components/StreamingMarkdownContent.tsx | 112 ++++++------- app/ui/app/src/index.css | 14 ++ app/ui/app/src/lib/highlighter.ts | 156 ++++++++++++++++++ 3 files changed, 222 insertions(+), 60 deletions(-) create mode 100644 app/ui/app/src/lib/highlighter.ts diff --git a/app/ui/app/src/components/StreamingMarkdownContent.tsx b/app/ui/app/src/components/StreamingMarkdownContent.tsx index a343140b..5f97a397 100644 --- a/app/ui/app/src/components/StreamingMarkdownContent.tsx +++ b/app/ui/app/src/components/StreamingMarkdownContent.tsx @@ -2,7 +2,8 @@ import React from "react"; import { Streamdown, defaultRemarkPlugins } from "streamdown"; import remarkCitationParser from "@/utils/remarkCitationParser"; import CopyButton from "./CopyButton"; -import { codeToTokens, type BundledLanguage } from "shiki"; +import type { BundledLanguage } from "shiki"; +import { highlighter } from "@/lib/highlighter"; interface StreamingMarkdownContentProps { content: string; @@ -30,9 +31,6 @@ const extractText = (node: React.ReactNode): string => { const CodeBlock = React.memo( ({ children }: React.HTMLAttributes) => { - const [lightTokens, setLightTokens] = React.useState(null); - const [darkTokens, setDarkTokens] = React.useState(null); - // Extract code and language from children const codeElement = children as React.ReactElement<{ className?: string; @@ -42,26 +40,25 @@ const CodeBlock = React.memo( codeElement.props.className?.replace(/language-/, "") || ""; const codeText = extractText(codeElement.props.children); - React.useEffect(() => { - async function highlight() { - try { - const [light, dark] = await Promise.all([ - codeToTokens(codeText, { - lang: language as BundledLanguage, - theme: "github-light", - }), - codeToTokens(codeText, { - lang: language as BundledLanguage, - theme: "github-dark", - }), - ]); - setLightTokens(light); - setDarkTokens(dark); - } catch (error) { - console.error("Failed to highlight code:", error); - } + // Synchronously highlight code using the pre-loaded highlighter + const tokens = React.useMemo(() => { + if (!highlighter) return null; + + try { + return { + light: highlighter.codeToTokensBase(codeText, { + lang: language as BundledLanguage, + theme: "one-light" as any, + }), + dark: highlighter.codeToTokensBase(codeText, { + lang: language as BundledLanguage, + theme: "one-dark" as any, + }), + }; + } catch (error) { + console.error("Failed to highlight code:", error); + return null; } - highlight(); }, [codeText, language]); return ( @@ -81,8 +78,8 @@ const CodeBlock = React.memo( {/* Light mode */}
           
-            {lightTokens
-              ? lightTokens.tokens.map((line: any, i: number) => (
+            {tokens?.light
+              ? tokens.light.map((line: any, i: number) => (
                   
                     {line.map((token: any, j: number) => (
                       
                     ))}
-                    {i < lightTokens.tokens.length - 1 && "\n"}
+                    {i < tokens.light.length - 1 && "\n"}
                   
                 ))
               : codeText}
@@ -103,8 +100,8 @@ const CodeBlock = React.memo(
         {/* Dark mode */}
         
           
-            {darkTokens
-              ? darkTokens.tokens.map((line: any, i: number) => (
+            {tokens?.dark
+              ? tokens.dark.map((line: any, i: number) => (
                   
                     {line.map((token: any, j: number) => (
                       
                     ))}
-                    {i < darkTokens.tokens.length - 1 && "\n"}
+                    {i < tokens.dark.length - 1 && "\n"}
                   
                 ))
               : codeText}
@@ -158,6 +155,26 @@ const StreamingMarkdownContent: React.FC =
           prose-pre:my-0
           prose-pre:max-w-full
           prose-pre:pt-1
+          [&_table]:border-collapse
+          [&_table]:w-full
+          [&_table]:border
+          [&_table]:border-neutral-200
+          [&_table]:rounded-lg
+          [&_table]:overflow-hidden
+          [&_th]:px-3
+          [&_th]:py-2
+          [&_th]:text-left
+          [&_th]:font-semibold
+          [&_th]:border-b
+          [&_th]:border-r
+          [&_th]:border-neutral-200
+          [&_th:last-child]:border-r-0
+          [&_td]:px-3
+          [&_td]:py-2
+          [&_td]:border-r
+          [&_td]:border-neutral-200
+          [&_td:last-child]:border-r-0
+          [&_tbody_tr:not(:last-child)_td]:border-b
           [&_code:not(pre_code)]:text-neutral-700
           [&_code:not(pre_code)]:bg-neutral-100
           [&_code:not(pre_code)]:font-normal
@@ -174,6 +191,10 @@ const StreamingMarkdownContent: React.FC =
           dark:prose-strong:text-neutral-200
           dark:prose-pre:text-neutral-200
           dark:prose:pre:text-neutral-200
+          dark:[&_table]:border-neutral-700
+          dark:[&_thead]:bg-neutral-800
+          dark:[&_th]:border-neutral-700
+          dark:[&_td]:border-neutral-700
           dark:[&_code:not(pre_code)]:text-neutral-200
           dark:[&_code:not(pre_code)]:bg-neutral-800
           dark:[&_code:not(pre_code)]:font-normal
@@ -190,6 +211,7 @@ const StreamingMarkdownContent: React.FC =
             parseIncompleteMarkdown={isStreaming}
             isAnimating={isStreaming}
             remarkPlugins={remarkPlugins}
+            disableTableActions={true}
             components={{
               pre: CodeBlock,
               table: ({
@@ -199,42 +221,12 @@ const StreamingMarkdownContent: React.FC =
                 
{children}
), - thead: ({ - children, - ...props - }: React.HTMLAttributes) => ( - - {children} - - ), - th: ({ - children, - ...props - }: React.HTMLAttributes) => ( - - {children} - - ), - td: ({ - children, - ...props - }: React.HTMLAttributes) => ( - - {children} - - ), // @ts-expect-error: custom citation type "ol-citation": ({ cursor, diff --git a/app/ui/app/src/index.css b/app/ui/app/src/index.css index af1f265d..f1768b3f 100644 --- a/app/ui/app/src/index.css +++ b/app/ui/app/src/index.css @@ -28,3 +28,17 @@ opacity: 1; } } + +/* Hide Streamdown table action buttons */ +.prose button[title="Copy table as markdown"], +.prose button[title="Download table"] { + display: none !important; +} + +/* Hide the parent div if it only contains these buttons */ +.prose + div:has(> button[title="Copy table as markdown"]):has( + > button[title="Download table"] + ) { + display: none !important; +} diff --git a/app/ui/app/src/lib/highlighter.ts b/app/ui/app/src/lib/highlighter.ts new file mode 100644 index 00000000..279bc746 --- /dev/null +++ b/app/ui/app/src/lib/highlighter.ts @@ -0,0 +1,156 @@ +import { createHighlighter } from "shiki"; +import type { ThemeRegistration } from "shiki"; + +const oneLightTheme: ThemeRegistration = { + name: "one-light", + type: "light", + colors: { + "editor.background": "#fafafa", + "editor.foreground": "#383a42", + }, + tokenColors: [ + { + scope: ["comment", "punctuation.definition.comment"], + settings: { foreground: "#a0a1a7" }, + }, + { + scope: ["keyword", "storage.type", "storage.modifier"], + settings: { foreground: "#a626a4" }, + }, + { scope: ["string", "string.quoted"], settings: { foreground: "#50a14f" } }, + { + scope: ["function", "entity.name.function", "support.function"], + settings: { foreground: "#4078f2" }, + }, + { + scope: [ + "constant.numeric", + "constant.language", + "constant.character", + "number", + ], + settings: { foreground: "#c18401" }, + }, + { + scope: ["variable", "support.variable"], + settings: { foreground: "#e45649" }, + }, + { + scope: ["entity.name.tag", "entity.name.type", "entity.name.class"], + settings: { foreground: "#e45649" }, + }, + { + scope: ["entity.other.attribute-name"], + settings: { foreground: "#c18401" }, + }, + { + scope: ["keyword.operator", "operator"], + settings: { foreground: "#a626a4" }, + }, + { scope: ["punctuation"], settings: { foreground: "#383a42" } }, + { + scope: ["markup.heading"], + settings: { foreground: "#e45649", fontStyle: "bold" }, + }, + { + scope: ["markup.bold"], + settings: { foreground: "#c18401", fontStyle: "bold" }, + }, + { + scope: ["markup.italic"], + settings: { foreground: "#a626a4", fontStyle: "italic" }, + }, + ], +}; + +const oneDarkTheme: ThemeRegistration = { + name: "one-dark", + type: "dark", + colors: { + "editor.background": "#282c34", + "editor.foreground": "#abb2bf", + }, + tokenColors: [ + { + scope: ["comment", "punctuation.definition.comment"], + settings: { foreground: "#5c6370" }, + }, + { + scope: ["keyword", "storage.type", "storage.modifier"], + settings: { foreground: "#c678dd" }, + }, + { scope: ["string", "string.quoted"], settings: { foreground: "#98c379" } }, + { + scope: ["function", "entity.name.function", "support.function"], + settings: { foreground: "#61afef" }, + }, + { + scope: [ + "constant.numeric", + "constant.language", + "constant.character", + "number", + ], + settings: { foreground: "#d19a66" }, + }, + { + scope: ["variable", "support.variable"], + settings: { foreground: "#e06c75" }, + }, + { + scope: ["entity.name.tag", "entity.name.type", "entity.name.class"], + settings: { foreground: "#e06c75" }, + }, + { + scope: ["entity.other.attribute-name"], + settings: { foreground: "#d19a66" }, + }, + { + scope: ["keyword.operator", "operator"], + settings: { foreground: "#c678dd" }, + }, + { scope: ["punctuation"], settings: { foreground: "#abb2bf" } }, + { + scope: ["markup.heading"], + settings: { foreground: "#e06c75", fontStyle: "bold" }, + }, + { + scope: ["markup.bold"], + settings: { foreground: "#d19a66", fontStyle: "bold" }, + }, + { + scope: ["markup.italic"], + settings: { foreground: "#c678dd", fontStyle: "italic" }, + }, + ], +}; + +export let highlighter: Awaited> | null = + null; + +export const highlighterPromise = createHighlighter({ + themes: [oneLightTheme, oneDarkTheme], + langs: [ + "javascript", + "typescript", + "python", + "bash", + "shell", + "json", + "html", + "css", + "tsx", + "jsx", + "go", + "rust", + "java", + "c", + "cpp", + "sql", + "yaml", + "markdown", + ], +}).then((h) => { + highlighter = h; + return h; +}); From a534d4e9e176e32734f59c5ae8c2fb6d93a11058 Mon Sep 17 00:00:00 2001 From: Eva Ho Date: Wed, 5 Nov 2025 13:12:31 -0500 Subject: [PATCH 03/32] fixing thinking not scrolling issue --- app/ui/app/src/components/Thinking.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/ui/app/src/components/Thinking.tsx b/app/ui/app/src/components/Thinking.tsx index a5ad3dc6..a725ce9c 100644 --- a/app/ui/app/src/components/Thinking.tsx +++ b/app/ui/app/src/components/Thinking.tsx @@ -73,8 +73,9 @@ export default function Thinking({ // Calculate max height for smooth animations const getMaxHeight = () => { if (isCollapsed) { - return finishedThinking ? "0px" : "12rem"; // 8rem = 128px (same as max-h-32) + return finishedThinking ? "0px" : "12rem"; } + // When expanded, use the content height or grow naturally return contentHeight ? `${contentHeight}px` : "none"; }; @@ -131,10 +132,11 @@ export default function Thinking({
From 536c987c39c51411d067c2f640165ac5bc0e929b Mon Sep 17 00:00:00 2001 From: Eva Ho Date: Wed, 5 Nov 2025 20:19:34 -0500 Subject: [PATCH 04/32] address comment --- app/ui/app/src/components/Thinking.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/ui/app/src/components/Thinking.tsx b/app/ui/app/src/components/Thinking.tsx index a725ce9c..a0c825d3 100644 --- a/app/ui/app/src/components/Thinking.tsx +++ b/app/ui/app/src/components/Thinking.tsx @@ -134,7 +134,7 @@ export default function Thinking({ ref={wrapperRef} className={`text-xs text-neutral-500 dark:text-neutral-500 rounded-md transition-[max-height,opacity] duration-300 ease-in-out relative ml-6 mt-2 - ${isCollapsed ? "overflow-hidden" : "overflow-y-auto max-h-28"}`} + ${isCollapsed ? "overflow-hidden" : activelyThinking ? "overflow-y-auto max-h-28" : "overflow-y-auto"}`} style={{ maxHeight: isCollapsed ? getMaxHeight() : undefined, opacity: isCollapsed && finishedThinking ? 0 : 1, From e309c80474a926a99b78e66fc7f5a3f5eca542d9 Mon Sep 17 00:00:00 2001 From: Eva Ho Date: Thu, 6 Nov 2025 13:49:59 -0500 Subject: [PATCH 05/32] address comments --- .../src/components/StreamingMarkdownContent.tsx | 2 +- app/ui/app/src/index.css | 14 -------------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/app/ui/app/src/components/StreamingMarkdownContent.tsx b/app/ui/app/src/components/StreamingMarkdownContent.tsx index 5f97a397..249a203a 100644 --- a/app/ui/app/src/components/StreamingMarkdownContent.tsx +++ b/app/ui/app/src/components/StreamingMarkdownContent.tsx @@ -211,7 +211,7 @@ const StreamingMarkdownContent: React.FC = parseIncompleteMarkdown={isStreaming} isAnimating={isStreaming} remarkPlugins={remarkPlugins} - disableTableActions={true} + controls={false} components={{ pre: CodeBlock, table: ({ diff --git a/app/ui/app/src/index.css b/app/ui/app/src/index.css index f1768b3f..af1f265d 100644 --- a/app/ui/app/src/index.css +++ b/app/ui/app/src/index.css @@ -28,17 +28,3 @@ opacity: 1; } } - -/* Hide Streamdown table action buttons */ -.prose button[title="Copy table as markdown"], -.prose button[title="Download table"] { - display: none !important; -} - -/* Hide the parent div if it only contains these buttons */ -.prose - div:has(> button[title="Copy table as markdown"]):has( - > button[title="Download table"] - ) { - display: none !important; -} From 3501a4bdf95c57ed102bce30836caae3466166ff Mon Sep 17 00:00:00 2001 From: Eva Ho Date: Thu, 6 Nov 2025 16:49:22 -0500 Subject: [PATCH 06/32] address comment --- app/ui/app/src/components/Thinking.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/ui/app/src/components/Thinking.tsx b/app/ui/app/src/components/Thinking.tsx index a0c825d3..7ab23e72 100644 --- a/app/ui/app/src/components/Thinking.tsx +++ b/app/ui/app/src/components/Thinking.tsx @@ -134,7 +134,7 @@ export default function Thinking({ ref={wrapperRef} className={`text-xs text-neutral-500 dark:text-neutral-500 rounded-md transition-[max-height,opacity] duration-300 ease-in-out relative ml-6 mt-2 - ${isCollapsed ? "overflow-hidden" : activelyThinking ? "overflow-y-auto max-h-28" : "overflow-y-auto"}`} + ${isCollapsed ? "overflow-hidden" : "overflow-y-auto"}`} style={{ maxHeight: isCollapsed ? getMaxHeight() : undefined, opacity: isCollapsed && finishedThinking ? 0 : 1, From e10a3533a5c6c2eb4ab266de1631907bcfd64d73 Mon Sep 17 00:00:00 2001 From: Bruce MacDonald Date: Sat, 8 Nov 2025 13:28:18 -0800 Subject: [PATCH 07/32] app/docs: remove out of date storybook instructions (#13006) --- app/README.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/app/README.md b/app/README.md index fcacc07a..eadf3447 100644 --- a/app/README.md +++ b/app/README.md @@ -48,16 +48,6 @@ The `-dev` flag enables: - CORS headers for cross-origin requests - Hot-reload support for UI development -#### Run Storybook - -Inside the `ui/app` directory, run: - -```bash -npm run storybook -``` - -For now we're writing stories as siblings of the component they're testing. So for example, `src/components/Message.stories.tsx` is the story for `src/components/Message.tsx`. - ## Build From 2aaf29acb5caf3f1fdc50cd48542c94df801752f Mon Sep 17 00:00:00 2001 From: Eva Ho Date: Mon, 10 Nov 2025 19:05:00 -0500 Subject: [PATCH 08/32] app/ui: do not send to prevent errors with cloud provider --- app/ui/ui.go | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/app/ui/ui.go b/app/ui/ui.go index fce58895..4cacc3c2 100644 --- a/app/ui/ui.go +++ b/app/ui/ui.go @@ -782,6 +782,25 @@ func (s *Server) chat(w http.ResponseWriter, r *http.Request) error { var thinkValue any if req.Think != nil { + // Validate that the model supports thinking if requested + thinkRequested := false + switch v := req.Think.(type) { + case bool: + thinkRequested = v + case string: + thinkRequested = v != "" && v != "none" + } + + if thinkRequested && !think { + errorEvent := responses.ErrorEvent{ + EventName: "error", + Error: fmt.Sprintf("Model %q does not support thinking/reasoning", req.Model), + Code: "model_capability_error", + } + json.NewEncoder(w).Encode(errorEvent) + flusher.Flush() + return nil + } thinkValue = req.Think } else { thinkValue = think @@ -866,6 +885,9 @@ func (s *Server) chat(w http.ResponseWriter, r *http.Request) error { return err } + // Debug: Log what we're sending + s.log().Debug("sending chat request", "model", chatReq.Model, "think", chatReq.Think, "num_messages", len(chatReq.Messages)) + err = c.Chat(ctx, chatReq, func(res api.ChatResponse) error { if loading { // Remove the loading indicator on first token @@ -1794,13 +1816,14 @@ func (s *Server) buildChatRequest(chat *store.Chat, model string, think any, ava var thinkValue *api.ThinkValue if think != nil { + // Only set Think if it's actually requesting thinking if boolValue, ok := think.(bool); ok { - thinkValue = &api.ThinkValue{ - Value: boolValue, + if boolValue { + thinkValue = &api.ThinkValue{Value: boolValue} } } else if stringValue, ok := think.(string); ok { - thinkValue = &api.ThinkValue{ - Value: stringValue, + if stringValue != "" && stringValue != "none" { + thinkValue = &api.ThinkValue{Value: stringValue} } } } From 6a818b8a094c2f53775d877f38f7f855346c77a7 Mon Sep 17 00:00:00 2001 From: Eva Ho Date: Mon, 10 Nov 2025 19:08:42 -0500 Subject: [PATCH 09/32] clean up --- app/ui/ui.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/ui/ui.go b/app/ui/ui.go index 4cacc3c2..631066e9 100644 --- a/app/ui/ui.go +++ b/app/ui/ui.go @@ -885,9 +885,6 @@ func (s *Server) chat(w http.ResponseWriter, r *http.Request) error { return err } - // Debug: Log what we're sending - s.log().Debug("sending chat request", "model", chatReq.Model, "think", chatReq.Think, "num_messages", len(chatReq.Messages)) - err = c.Chat(ctx, chatReq, func(res api.ChatResponse) error { if loading { // Remove the loading indicator on first token From 9d615cdaa0f1eb96491b672c9e48f999338dffa5 Mon Sep 17 00:00:00 2001 From: Eva Ho Date: Mon, 10 Nov 2025 20:13:50 -0500 Subject: [PATCH 10/32] fix test --- app/ui/ui.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/ui/ui.go b/app/ui/ui.go index 631066e9..a05ecf0f 100644 --- a/app/ui/ui.go +++ b/app/ui/ui.go @@ -790,7 +790,7 @@ func (s *Server) chat(w http.ResponseWriter, r *http.Request) error { case string: thinkRequested = v != "" && v != "none" } - + if thinkRequested && !think { errorEvent := responses.ErrorEvent{ EventName: "error", From 6df42088367f7b5587478d610e8d62466b6afd1d Mon Sep 17 00:00:00 2001 From: Sheikh <50134239+cybardev@users.noreply.github.com> Date: Tue, 11 Nov 2025 01:51:22 -0400 Subject: [PATCH 11/32] docs: fix metal gpu section header (#13045) --- docs/gpu.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/gpu.mdx b/docs/gpu.mdx index 2f20c627..bf114221 100644 --- a/docs/gpu.mdx +++ b/docs/gpu.mdx @@ -121,6 +121,6 @@ In some Linux distributions, SELinux can prevent containers from accessing the AMD GPU devices. On the host system you can run `sudo setsebool container_use_devices=1` to allow containers to use devices. -### Metal (Apple GPUs) +## Metal (Apple GPUs) Ollama supports GPU acceleration on Apple devices via the Metal API. From 2a9b61f099b5f5307b3af463bd6b1464941260fd Mon Sep 17 00:00:00 2001 From: Eva Ho Date: Tue, 11 Nov 2025 08:58:55 -0500 Subject: [PATCH 12/32] address comment --- app/ui/app/src/api.ts | 9 ++++++++- app/ui/ui.go | 19 ------------------- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/app/ui/app/src/api.ts b/app/ui/app/src/api.ts index c8b2e116..4158bafc 100644 --- a/app/ui/app/src/api.ts +++ b/app/ui/app/src/api.ts @@ -205,6 +205,13 @@ export async function* sendMessage( data: uint8ArrayToBase64(att.data), })); + // Only send think parameter when actually requesting thinking + // Don't send false as it causes issues with some providers + const shouldSendThink = + think !== undefined && + ((typeof think === "boolean" && think) || + (typeof think === "string" && think !== "")); + const response = await fetch(`${API_BASE}/api/v1/chat/${chatId}`, { method: "POST", headers: { @@ -222,7 +229,7 @@ export async function* sendMessage( web_search: webSearch ?? false, file_tools: fileTools ?? false, ...(forceUpdate !== undefined ? { forceUpdate } : {}), - ...(think !== undefined ? { think } : {}), + ...(shouldSendThink ? { think } : {}), }), ), signal, diff --git a/app/ui/ui.go b/app/ui/ui.go index a05ecf0f..1d0e2579 100644 --- a/app/ui/ui.go +++ b/app/ui/ui.go @@ -782,25 +782,6 @@ func (s *Server) chat(w http.ResponseWriter, r *http.Request) error { var thinkValue any if req.Think != nil { - // Validate that the model supports thinking if requested - thinkRequested := false - switch v := req.Think.(type) { - case bool: - thinkRequested = v - case string: - thinkRequested = v != "" && v != "none" - } - - if thinkRequested && !think { - errorEvent := responses.ErrorEvent{ - EventName: "error", - Error: fmt.Sprintf("Model %q does not support thinking/reasoning", req.Model), - Code: "model_capability_error", - } - json.NewEncoder(w).Encode(errorEvent) - flusher.Flush() - return nil - } thinkValue = req.Think } else { thinkValue = think From 59241c5bee60bc49e96589a7a7482f964fe7ffe0 Mon Sep 17 00:00:00 2001 From: Baptiste Jamin Date: Tue, 11 Nov 2025 17:49:50 +0100 Subject: [PATCH 13/32] server: add logprobs and top_logprobs support to Ollama's API (#12899) Adds logprobs support to Ollama's API including support for Ollama's OpenAI-compatible API. By specifying the new 'logprobs' boolean parameter in the API, Ollama will return the log probabilities for each token generated. 'top_logprobs', an integer value can also be specified up to the value 20. When specified, the API will also provide the number of most likely tokens to return at each token position Co-authored-by: Baptiste Jamin --- api/types.go | 42 +++ integration/api_test.go | 171 +++++++++++ llama/llama.go | 13 + llm/server.go | 24 +- openai/openai.go | 53 +++- openai/openai_test.go | 216 ++++++++++++++ runner/common/logprob.go | 79 ++++++ runner/common/logprob_test.go | 498 +++++++++++++++++++++++++++++++++ runner/llamarunner/runner.go | 57 +++- runner/ollamarunner/runner.go | 89 ++++-- server/logprob.go | 29 ++ server/routes.go | 63 ++++- server/routes_generate_test.go | 80 ++++++ 13 files changed, 1367 insertions(+), 47 deletions(-) create mode 100644 runner/common/logprob.go create mode 100644 runner/common/logprob_test.go create mode 100644 server/logprob.go diff --git a/api/types.go b/api/types.go index 1615fce6..d5788d54 100644 --- a/api/types.go +++ b/api/types.go @@ -117,6 +117,14 @@ type GenerateRequest struct { // DebugRenderOnly is a debug option that, when set to true, returns the rendered // template instead of calling the model. DebugRenderOnly bool `json:"_debug_render_only,omitempty"` + + // Logprobs specifies whether to return log probabilities of the output tokens. + Logprobs bool `json:"logprobs,omitempty"` + + // TopLogprobs is the number of most likely tokens to return at each token position, + // each with an associated log probability. Only applies when Logprobs is true. + // Valid values are 0-20. Default is 0 (only return the selected token's logprob). + TopLogprobs int `json:"top_logprobs,omitempty"` } // ChatRequest describes a request sent by [Client.Chat]. @@ -159,6 +167,14 @@ type ChatRequest struct { // DebugRenderOnly is a debug option that, when set to true, returns the rendered // template instead of calling the model. DebugRenderOnly bool `json:"_debug_render_only,omitempty"` + + // Logprobs specifies whether to return log probabilities of the output tokens. + Logprobs bool `json:"logprobs,omitempty"` + + // TopLogprobs is the number of most likely tokens to return at each token position, + // each with an associated log probability. Only applies when Logprobs is true. + // Valid values are 0-20. Default is 0 (only return the selected token's logprob). + TopLogprobs int `json:"top_logprobs,omitempty"` } type Tools []Tool @@ -343,6 +359,24 @@ func (t *ToolFunction) String() string { return string(bts) } +// TokenLogprob represents log probability information for a single token alternative. +type TokenLogprob struct { + // Token is the text representation of the token. + Token string `json:"token"` + + // Logprob is the log probability of this token. + Logprob float64 `json:"logprob"` +} + +// Logprob contains log probability information for a generated token. +type Logprob struct { + TokenLogprob + + // TopLogprobs contains the most likely tokens and their log probabilities + // at this position, if requested via TopLogprobs parameter. + TopLogprobs []TokenLogprob `json:"top_logprobs,omitempty"` +} + // ChatResponse is the response returned by [Client.Chat]. Its fields are // similar to [GenerateResponse]. type ChatResponse struct { @@ -369,6 +403,10 @@ type ChatResponse struct { DebugInfo *DebugInfo `json:"_debug_info,omitempty"` + // Logprobs contains log probability information for the generated tokens, + // if requested via the Logprobs parameter. + Logprobs []Logprob `json:"logprobs,omitempty"` + Metrics } @@ -677,6 +715,10 @@ type GenerateResponse struct { ToolCalls []ToolCall `json:"tool_calls,omitempty"` DebugInfo *DebugInfo `json:"_debug_info,omitempty"` + + // Logprobs contains log probability information for the generated tokens, + // if requested via the Logprobs parameter. + Logprobs []Logprob `json:"logprobs,omitempty"` } // ModelDetails provides details about a model. diff --git a/integration/api_test.go b/integration/api_test.go index 39eea39c..839e14d7 100644 --- a/integration/api_test.go +++ b/integration/api_test.go @@ -381,3 +381,174 @@ func TestAPIShowModel(t *testing.T) { t.Errorf("%s missing modified_at: %#v", modelName, resp) } } + +func TestAPIGenerateLogprobs(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + client, _, cleanup := InitServerConnection(ctx, t) + defer cleanup() + + if err := PullIfMissing(ctx, client, smol); err != nil { + t.Fatalf("pull failed %s", err) + } + + enableLogprobs := true + noStream := false + + tests := []struct { + name string + logprobs *bool + topLogprobs int + expectCount int + }{ + { + name: "no_logprobs", + logprobs: nil, + topLogprobs: 0, + expectCount: 0, + }, + { + name: "logprobs_only", + logprobs: &enableLogprobs, + topLogprobs: 0, + expectCount: 1, + }, + { + name: "logprobs_with_top_5", + logprobs: &enableLogprobs, + topLogprobs: 5, + expectCount: 1, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + req := api.GenerateRequest{ + Model: smol, + Prompt: "Why is the sky blue?", + Stream: &noStream, + Logprobs: test.logprobs != nil && *test.logprobs, + TopLogprobs: test.topLogprobs, + Options: map[string]interface{}{ + "temperature": 0, + "seed": 123, + "num_predict": 10, + }, + } + + var response api.GenerateResponse + err := client.Generate(ctx, &req, func(resp api.GenerateResponse) error { + if resp.Done { + response = resp + } + return nil + }) + if err != nil { + t.Fatalf("generate failed: %s", err) + } + + // Check logprobs based on expectation + if test.expectCount == 0 { + if len(response.Logprobs) > 0 { + t.Errorf("expected no logprobs but got %d", len(response.Logprobs)) + } + } else { + if len(response.Logprobs) == 0 { + t.Errorf("expected logprobs but got none") + } + + // Validate each logprob entry + for i, lp := range response.Logprobs { + if lp.Token == "" { + t.Errorf("logprob[%d] has empty token", i) + } + if lp.Logprob > 0 { + t.Errorf("logprob[%d] has positive logprob %f (should be <= 0)", i, lp.Logprob) + } + + // Check top_logprobs if requested + if test.topLogprobs > 0 { + if len(lp.TopLogprobs) == 0 { + t.Errorf("logprob[%d] expected top_logprobs but got none", i) + } + if len(lp.TopLogprobs) > test.topLogprobs { + t.Errorf("logprob[%d] has %d top_logprobs, expected max %d", i, len(lp.TopLogprobs), test.topLogprobs) + } + + // Verify top_logprobs are sorted by probability (descending) + for j := 1; j < len(lp.TopLogprobs); j++ { + if lp.TopLogprobs[j-1].Logprob < lp.TopLogprobs[j].Logprob { + t.Errorf("logprob[%d].top_logprobs not sorted: %f < %f", i, lp.TopLogprobs[j-1].Logprob, lp.TopLogprobs[j].Logprob) + } + } + } else if len(lp.TopLogprobs) > 0 { + t.Errorf("logprob[%d] has top_logprobs but none were requested", i) + } + } + } + }) + } +} + +func TestAPIChatLogprobs(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + client, _, cleanup := InitServerConnection(ctx, t) + defer cleanup() + + if err := PullIfMissing(ctx, client, smol); err != nil { + t.Fatalf("pull failed %s", err) + } + + enableLogprobs := true + noStream := false + + req := api.ChatRequest{ + Model: smol, + Messages: []api.Message{ + {Role: "user", Content: "Say hello in one word"}, + }, + Stream: &noStream, + Logprobs: enableLogprobs, + TopLogprobs: 3, + Options: map[string]interface{}{ + "temperature": 0, + "seed": 123, + "num_predict": 5, + }, + } + + var response api.ChatResponse + err := client.Chat(ctx, &req, func(resp api.ChatResponse) error { + if resp.Done { + response = resp + } + return nil + }) + if err != nil { + t.Fatalf("chat failed: %s", err) + } + + if len(response.Logprobs) == 0 { + t.Fatal("expected logprobs in response but got none") + } + + t.Logf("received %d logprobs for chat response", len(response.Logprobs)) + + for i, lp := range response.Logprobs { + if lp.Token == "" { + t.Errorf("logprob[%d] has empty token", i) + } + if lp.Logprob > 0 { + t.Errorf("logprob[%d] has positive logprob %f", i, lp.Logprob) + } + if len(lp.TopLogprobs) == 0 { + t.Errorf("logprob[%d] expected top_logprobs but got none", i) + } + if len(lp.TopLogprobs) > 3 { + t.Errorf("logprob[%d] has %d top_logprobs, expected max 3", i, len(lp.TopLogprobs)) + } + } +} diff --git a/llama/llama.go b/llama/llama.go index c995b3ea..f8a051ea 100644 --- a/llama/llama.go +++ b/llama/llama.go @@ -217,6 +217,19 @@ func (c *Context) GetEmbeddingsIth(i int) []float32 { return embeddings } +// GetLogitsIth gets the logits for the ith token +func (c *Context) GetLogitsIth(i int) []float32 { + logits := unsafe.Pointer(C.llama_get_logits_ith(c.c, C.int32_t(i))) + if logits == nil { + return nil + } + + vocabSize := c.Model().NumVocab() + result := make([]float32, vocabSize) + _ = copy(result, unsafe.Slice((*float32)(logits), vocabSize)) + return result +} + type ModelParams struct { NumGpuLayers int MainGpu int diff --git a/llm/server.go b/llm/server.go index 4f7c3760..87f97a01 100644 --- a/llm/server.go +++ b/llm/server.go @@ -1362,6 +1362,12 @@ type CompletionRequest struct { Grammar string // set before sending the request to the subprocess Shift bool Truncate bool + + // Logprobs specifies whether to include log probabilities in the response + Logprobs bool + + // TopLogprobs specifies the number of most likely alternative tokens to return (0-20) + TopLogprobs int } // DoneReason represents the reason why a completion response is done @@ -1387,6 +1393,18 @@ func (d DoneReason) String() string { } } +// TokenLogprob represents log probability information for a single token alternative. +type TokenLogprob struct { + Token string `json:"token"` + Logprob float64 `json:"logprob"` +} + +// Logprob contains log probability information for a generated token. +type Logprob struct { + TokenLogprob + TopLogprobs []TokenLogprob `json:"top_logprobs,omitempty"` +} + type CompletionResponse struct { Content string `json:"content"` DoneReason DoneReason `json:"done_reason"` @@ -1395,6 +1413,9 @@ type CompletionResponse struct { PromptEvalDuration time.Duration `json:"prompt_eval_duration"` EvalCount int `json:"eval_count"` EvalDuration time.Duration `json:"eval_duration"` + + // Logprobs contains log probability information if requested + Logprobs []Logprob `json:"logprobs,omitempty"` } func (s *llmServer) Completion(ctx context.Context, req CompletionRequest, fn func(CompletionResponse)) error { @@ -1530,7 +1551,8 @@ func (s *llmServer) Completion(ctx context.Context, req CompletionRequest, fn fu if c.Content != "" { fn(CompletionResponse{ - Content: c.Content, + Content: c.Content, + Logprobs: c.Logprobs, }) } diff --git a/openai/openai.go b/openai/openai.go index d4fd26c2..4713d481 100644 --- a/openai/openai.go +++ b/openai/openai.go @@ -40,22 +40,29 @@ type Message struct { ToolCallID string `json:"tool_call_id,omitempty"` } +type ChoiceLogprobs struct { + Content []api.Logprob `json:"content"` +} + type Choice struct { - Index int `json:"index"` - Message Message `json:"message"` - FinishReason *string `json:"finish_reason"` + Index int `json:"index"` + Message Message `json:"message"` + FinishReason *string `json:"finish_reason"` + Logprobs *ChoiceLogprobs `json:"logprobs,omitempty"` } type ChunkChoice struct { - Index int `json:"index"` - Delta Message `json:"delta"` - FinishReason *string `json:"finish_reason"` + Index int `json:"index"` + Delta Message `json:"delta"` + FinishReason *string `json:"finish_reason"` + Logprobs *ChoiceLogprobs `json:"logprobs,omitempty"` } type CompleteChunkChoice struct { - Text string `json:"text"` - Index int `json:"index"` - FinishReason *string `json:"finish_reason"` + Text string `json:"text"` + Index int `json:"index"` + FinishReason *string `json:"finish_reason"` + Logprobs *ChoiceLogprobs `json:"logprobs,omitempty"` } type Usage struct { @@ -104,6 +111,8 @@ type ChatCompletionRequest struct { Tools []api.Tool `json:"tools"` Reasoning *Reasoning `json:"reasoning,omitempty"` ReasoningEffort *string `json:"reasoning_effort,omitempty"` + Logprobs *bool `json:"logprobs"` + TopLogprobs int `json:"top_logprobs"` DebugRenderOnly bool `json:"_debug_render_only"` } @@ -142,6 +151,7 @@ type CompletionRequest struct { Temperature *float32 `json:"temperature"` TopP float32 `json:"top_p"` Suffix string `json:"suffix"` + Logprobs *int `json:"logprobs"` DebugRenderOnly bool `json:"_debug_render_only"` } @@ -251,6 +261,12 @@ func ToToolCalls(tc []api.ToolCall) []ToolCall { // ToChatCompletion converts an api.ChatResponse to ChatCompletion func ToChatCompletion(id string, r api.ChatResponse) ChatCompletion { toolCalls := ToToolCalls(r.Message.ToolCalls) + + var logprobs *ChoiceLogprobs + if len(r.Logprobs) > 0 { + logprobs = &ChoiceLogprobs{Content: r.Logprobs} + } + return ChatCompletion{ Id: id, Object: "chat.completion", @@ -269,6 +285,7 @@ func ToChatCompletion(id string, r api.ChatResponse) ChatCompletion { } return nil }(r.DoneReason), + Logprobs: logprobs, }}, Usage: ToUsage(r), DebugInfo: r.DebugInfo, } @@ -277,6 +294,12 @@ func ToChatCompletion(id string, r api.ChatResponse) ChatCompletion { // ToChunk converts an api.ChatResponse to ChatCompletionChunk func ToChunk(id string, r api.ChatResponse, toolCallSent bool) ChatCompletionChunk { toolCalls := ToToolCalls(r.Message.ToolCalls) + + var logprobs *ChoiceLogprobs + if len(r.Logprobs) > 0 { + logprobs = &ChoiceLogprobs{Content: r.Logprobs} + } + return ChatCompletionChunk{ Id: id, Object: "chat.completion.chunk", @@ -295,6 +318,7 @@ func ToChunk(id string, r api.ChatResponse, toolCallSent bool) ChatCompletionChu } return nil }(r.DoneReason), + Logprobs: logprobs, }}, } } @@ -604,6 +628,8 @@ func FromChatRequest(r ChatCompletionRequest) (*api.ChatRequest, error) { Stream: &r.Stream, Tools: r.Tools, Think: think, + Logprobs: r.Logprobs != nil && *r.Logprobs, + TopLogprobs: r.TopLogprobs, DebugRenderOnly: r.DebugRenderOnly, }, nil } @@ -680,12 +706,21 @@ func FromCompleteRequest(r CompletionRequest) (api.GenerateRequest, error) { options["top_p"] = 1.0 } + var logprobs bool + var topLogprobs int + if r.Logprobs != nil && *r.Logprobs > 0 { + logprobs = true + topLogprobs = *r.Logprobs + } + return api.GenerateRequest{ Model: r.Model, Prompt: r.Prompt, Options: options, Stream: &r.Stream, Suffix: r.Suffix, + Logprobs: logprobs, + TopLogprobs: topLogprobs, DebugRenderOnly: r.DebugRenderOnly, }, nil } diff --git a/openai/openai_test.go b/openai/openai_test.go index 6a42f915..51e243de 100644 --- a/openai/openai_test.go +++ b/openai/openai_test.go @@ -3,6 +3,7 @@ package openai import ( "encoding/base64" "testing" + "time" "github.com/google/go-cmp/cmp" @@ -218,3 +219,218 @@ func TestToToolCallsPreservesIDs(t *testing.T) { t.Errorf("input tool calls mutated (-want +got):\n%s", diff) } } + +func TestFromChatRequest_WithLogprobs(t *testing.T) { + trueVal := true + + req := ChatCompletionRequest{ + Model: "test-model", + Messages: []Message{ + {Role: "user", Content: "Hello"}, + }, + Logprobs: &trueVal, + TopLogprobs: 5, + } + + result, err := FromChatRequest(req) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if !result.Logprobs { + t.Error("expected Logprobs to be true") + } + + if result.TopLogprobs != 5 { + t.Errorf("expected TopLogprobs to be 5, got %d", result.TopLogprobs) + } +} + +func TestFromChatRequest_LogprobsDefault(t *testing.T) { + req := ChatCompletionRequest{ + Model: "test-model", + Messages: []Message{ + {Role: "user", Content: "Hello"}, + }, + } + + result, err := FromChatRequest(req) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if result.Logprobs { + t.Error("expected Logprobs to be false by default") + } + + if result.TopLogprobs != 0 { + t.Errorf("expected TopLogprobs to be 0 by default, got %d", result.TopLogprobs) + } +} + +func TestFromCompleteRequest_WithLogprobs(t *testing.T) { + logprobsVal := 5 + + req := CompletionRequest{ + Model: "test-model", + Prompt: "Hello", + Logprobs: &logprobsVal, + } + + result, err := FromCompleteRequest(req) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if !result.Logprobs { + t.Error("expected Logprobs to be true") + } + + if result.TopLogprobs != 5 { + t.Errorf("expected TopLogprobs to be 5, got %d", result.TopLogprobs) + } +} + +func TestToChatCompletion_WithLogprobs(t *testing.T) { + createdAt := time.Unix(1234567890, 0) + resp := api.ChatResponse{ + Model: "test-model", + CreatedAt: createdAt, + Message: api.Message{Role: "assistant", Content: "Hello there"}, + Logprobs: []api.Logprob{ + { + TokenLogprob: api.TokenLogprob{ + Token: "Hello", + Logprob: -0.5, + }, + TopLogprobs: []api.TokenLogprob{ + {Token: "Hello", Logprob: -0.5}, + {Token: "Hi", Logprob: -1.2}, + }, + }, + { + TokenLogprob: api.TokenLogprob{ + Token: " there", + Logprob: -0.3, + }, + TopLogprobs: []api.TokenLogprob{ + {Token: " there", Logprob: -0.3}, + {Token: " world", Logprob: -1.5}, + }, + }, + }, + Done: true, + Metrics: api.Metrics{ + PromptEvalCount: 5, + EvalCount: 2, + }, + } + + id := "test-id" + + result := ToChatCompletion(id, resp) + + if result.Id != id { + t.Errorf("expected Id %q, got %q", id, result.Id) + } + + if result.Created != 1234567890 { + t.Errorf("expected Created %d, got %d", int64(1234567890), result.Created) + } + + if len(result.Choices) != 1 { + t.Fatalf("expected 1 choice, got %d", len(result.Choices)) + } + + choice := result.Choices[0] + if choice.Message.Content != "Hello there" { + t.Errorf("expected content %q, got %q", "Hello there", choice.Message.Content) + } + + if choice.Logprobs == nil { + t.Fatal("expected Logprobs to be present") + } + + if len(choice.Logprobs.Content) != 2 { + t.Fatalf("expected 2 logprobs, got %d", len(choice.Logprobs.Content)) + } + + // Verify first logprob + if choice.Logprobs.Content[0].Token != "Hello" { + t.Errorf("expected first token %q, got %q", "Hello", choice.Logprobs.Content[0].Token) + } + if choice.Logprobs.Content[0].Logprob != -0.5 { + t.Errorf("expected first logprob -0.5, got %f", choice.Logprobs.Content[0].Logprob) + } + if len(choice.Logprobs.Content[0].TopLogprobs) != 2 { + t.Errorf("expected 2 top_logprobs, got %d", len(choice.Logprobs.Content[0].TopLogprobs)) + } + + // Verify second logprob + if choice.Logprobs.Content[1].Token != " there" { + t.Errorf("expected second token %q, got %q", " there", choice.Logprobs.Content[1].Token) + } +} + +func TestToChatCompletion_WithoutLogprobs(t *testing.T) { + createdAt := time.Unix(1234567890, 0) + resp := api.ChatResponse{ + Model: "test-model", + CreatedAt: createdAt, + Message: api.Message{Role: "assistant", Content: "Hello"}, + Done: true, + Metrics: api.Metrics{ + PromptEvalCount: 5, + EvalCount: 1, + }, + } + + id := "test-id" + + result := ToChatCompletion(id, resp) + + if len(result.Choices) != 1 { + t.Fatalf("expected 1 choice, got %d", len(result.Choices)) + } + + // When no logprobs, Logprobs should be nil + if result.Choices[0].Logprobs != nil { + t.Error("expected Logprobs to be nil when not requested") + } +} + +func TestFromChatRequest_TopLogprobsRange(t *testing.T) { + tests := []struct { + name string + topLogprobs int + expectValid bool + }{ + {name: "valid: 0", topLogprobs: 0, expectValid: true}, + {name: "valid: 1", topLogprobs: 1, expectValid: true}, + {name: "valid: 10", topLogprobs: 10, expectValid: true}, + {name: "valid: 20", topLogprobs: 20, expectValid: true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + trueVal := true + req := ChatCompletionRequest{ + Model: "test-model", + Messages: []Message{ + {Role: "user", Content: "Hello"}, + }, + Logprobs: &trueVal, + TopLogprobs: tt.topLogprobs, + } + + result, err := FromChatRequest(req) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if result.TopLogprobs != tt.topLogprobs { + t.Errorf("expected TopLogprobs %d, got %d", tt.topLogprobs, result.TopLogprobs) + } + }) + } +} diff --git a/runner/common/logprob.go b/runner/common/logprob.go new file mode 100644 index 00000000..a0d764a3 --- /dev/null +++ b/runner/common/logprob.go @@ -0,0 +1,79 @@ +package common + +import ( + "math" + "sort" + + "github.com/ollama/ollama/llm" +) + +// TokenDecoderFunc is a function that converts token IDs to text. +type TokenDecoderFunc func(tokenID int) string + +// CalculateLogprobs converts raw logits to log probabilities and finds top K tokens. +// It uses numerically stable softmax to compute log probabilities. +func CalculateLogprobs(logits []float32, selectedToken int, topK int, decoder TokenDecoderFunc) []llm.Logprob { + if len(logits) == 0 { + return nil + } + + // Step 1: Convert logits to log probabilities using numerically stable softmax + maxLogit := logits[0] + for _, logit := range logits[1:] { + if logit > maxLogit { + maxLogit = logit + } + } + + var sumExp float64 + for _, logit := range logits { + sumExp += math.Exp(float64(logit - maxLogit)) + } + logSumExp := float32(math.Log(sumExp)) + + logProbs := make([]float32, len(logits)) + for i, logit := range logits { + logProbs[i] = (logit - maxLogit) - logSumExp + } + + // Step 2: Get selected token's information + selectedLogprob := logProbs[selectedToken] + selectedText := decoder(selectedToken) + + result := llm.Logprob{ + TokenLogprob: llm.TokenLogprob{ + Token: selectedText, + Logprob: float64(selectedLogprob), + }, + } + + // Step 3: If topK requested, find the top K tokens + if topK > 0 { + type tokenLogprobPair struct { + tokenID int + logprob float32 + } + + pairs := make([]tokenLogprobPair, len(logProbs)) + for i, lp := range logProbs { + pairs[i] = tokenLogprobPair{tokenID: i, logprob: lp} + } + + sort.Slice(pairs, func(i, j int) bool { + return pairs[i].logprob > pairs[j].logprob + }) + + k := min(topK, len(pairs)) + topLogprobs := make([]llm.TokenLogprob, k) + for i := range k { + tokenText := decoder(pairs[i].tokenID) + topLogprobs[i] = llm.TokenLogprob{ + Token: tokenText, + Logprob: float64(pairs[i].logprob), + } + } + result.TopLogprobs = topLogprobs + } + + return []llm.Logprob{result} +} diff --git a/runner/common/logprob_test.go b/runner/common/logprob_test.go new file mode 100644 index 00000000..c798f3f4 --- /dev/null +++ b/runner/common/logprob_test.go @@ -0,0 +1,498 @@ +package common + +import ( + "math" + "testing" + + "github.com/ollama/ollama/llm" +) + +func TestCalculateLogprobs(t *testing.T) { + tokens := map[int]string{ + 0: "hello", + 1: "hi", + 2: "hey", + 3: "world", + } + decoder := func(tokenID int) string { + if text, ok := tokens[tokenID]; ok { + return text + } + return "" + } + + tests := []struct { + name string + logits []float32 + selectedToken int + topK int + wantLen int + wantToken string + }{ + { + name: "Empty logits", + logits: []float32{}, + selectedToken: 0, + topK: 0, + wantLen: 0, + }, + { + name: "Single token without top logprobs", + logits: []float32{1.0, 0.5, 0.3, 0.1}, + selectedToken: 0, + topK: 0, + wantLen: 1, + wantToken: "hello", + }, + { + name: "Single token with top logprobs", + logits: []float32{1.0, 0.5, 0.3, 0.1}, + selectedToken: 0, + topK: 3, + wantLen: 1, + wantToken: "hello", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := CalculateLogprobs(tt.logits, tt.selectedToken, tt.topK, decoder) + if len(result) != tt.wantLen { + t.Errorf("CalculateLogprobs() returned %d results, want %d", len(result), tt.wantLen) + } + if tt.wantLen > 0 && result[0].Token != tt.wantToken { + t.Errorf("CalculateLogprobs() token = %s, want %s", result[0].Token, tt.wantToken) + } + if tt.topK > 0 && len(result) > 0 { + if len(result[0].TopLogprobs) != tt.topK { + t.Errorf("CalculateLogprobs() top logprobs count = %d, want %d", len(result[0].TopLogprobs), tt.topK) + } + } + }) + } +} + +func TestCalculateLogprobsNumericalStability(t *testing.T) { + tokens := map[int]string{ + 0: "a", + 1: "b", + 2: "c", + } + decoder := func(tokenID int) string { + if text, ok := tokens[tokenID]; ok { + return text + } + return "" + } + + // Test with very large logits to ensure numerical stability + logits := []float32{1000.0, 999.0, 998.0} + result := CalculateLogprobs(logits, 0, 3, decoder) + + if len(result) != 1 { + t.Fatalf("Expected 1 result, got %d", len(result)) + } + + // Check that log probabilities are finite and reasonable + if math.IsInf(result[0].Logprob, 0) || math.IsNaN(result[0].Logprob) { + t.Errorf("Selected token logprob is not finite: %f", result[0].Logprob) + } + + for i, tlp := range result[0].TopLogprobs { + if math.IsInf(tlp.Logprob, 0) || math.IsNaN(tlp.Logprob) { + t.Errorf("Top logprob[%d] is not finite: %f", i, tlp.Logprob) + } + } + + // Top logprobs should be in descending order + for i := 1; i < len(result[0].TopLogprobs); i++ { + if result[0].TopLogprobs[i].Logprob > result[0].TopLogprobs[i-1].Logprob { + t.Errorf("Top logprobs not in descending order: %f > %f", + result[0].TopLogprobs[i].Logprob, result[0].TopLogprobs[i-1].Logprob) + } + } +} + +func TestCalculateLogprobsProbabilityCorrectness(t *testing.T) { + tokens := map[int]string{ + 0: "hello", + 1: "world", + 2: "foo", + 3: "bar", + } + decoder := func(tokenID int) string { + if text, ok := tokens[tokenID]; ok { + return text + } + return "" + } + + tests := []struct { + name string + logits []float32 + selectedToken int + topK int + }{ + { + name: "Uniform logits", + logits: []float32{1.0, 1.0, 1.0, 1.0}, + selectedToken: 0, + topK: 4, + }, + { + name: "Different logits", + logits: []float32{2.0, 1.0, 0.5, 0.1}, + selectedToken: 0, + topK: 4, + }, + { + name: "Negative logits", + logits: []float32{-1.0, -2.0, -3.0, -4.0}, + selectedToken: 0, + topK: 4, + }, + { + name: "Mixed logits", + logits: []float32{5.0, -5.0, 0.0, 2.5}, + selectedToken: 0, + topK: 4, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := CalculateLogprobs(tt.logits, tt.selectedToken, tt.topK, decoder) + + if len(result) != 1 { + t.Fatalf("Expected 1 result, got %d", len(result)) + } + + // Verify all probabilities are non-positive (log probabilities should be <= 0) + if result[0].Logprob > 0 { + t.Errorf("Selected token logprob should be <= 0, got %f", result[0].Logprob) + } + + for i, tlp := range result[0].TopLogprobs { + if tlp.Logprob > 0 { + t.Errorf("Top logprob[%d] should be <= 0, got %f", i, tlp.Logprob) + } + } + + // Verify that probabilities sum to approximately 1 + // Sum of exp(logprob) for all tokens should equal 1 + var probSum float64 + for _, lp := range result[0].TopLogprobs { + probSum += math.Exp(lp.Logprob) + } + + // For uniform logits, each probability should be 1/n + if tt.name == "Uniform logits" { + expectedProb := 1.0 / float64(len(tt.logits)) + actualProb := math.Exp(result[0].Logprob) + if math.Abs(actualProb-expectedProb) > 1e-6 { + t.Errorf("For uniform logits, expected probability %f, got %f", + expectedProb, actualProb) + } + } + + // Verify top logprobs are sorted in descending order + for i := 1; i < len(result[0].TopLogprobs); i++ { + if result[0].TopLogprobs[i].Logprob > result[0].TopLogprobs[i-1].Logprob { + t.Errorf("Top logprobs not sorted: position %d (%f) > position %d (%f)", + i, result[0].TopLogprobs[i].Logprob, + i-1, result[0].TopLogprobs[i-1].Logprob) + } + } + + // Verify the selected token appears in top logprobs + selectedText := decoder(tt.selectedToken) + found := false + for _, tlp := range result[0].TopLogprobs { + if tlp.Token == selectedText { + found = true + // The logprob in top logprobs should match the selected token's logprob + if math.Abs(tlp.Logprob-result[0].Logprob) > 1e-6 { + t.Errorf("Selected token logprob mismatch: main=%f, in top=%f", + result[0].Logprob, tlp.Logprob) + } + break + } + } + if !found { + t.Errorf("Selected token %q not found in top logprobs", selectedText) + } + }) + } +} + +func TestCalculateLogprobsSoftmaxCorrectness(t *testing.T) { + // Test that softmax calculation is correct by verifying probabilities sum to 1 + decoder := func(tokenID int) string { + return string(rune('A' + tokenID)) + } + + tests := []struct { + name string + logits []float32 + }{ + { + name: "Small vocabulary", + logits: []float32{1.0, 2.0, 3.0}, + }, + { + name: "Large differences", + logits: []float32{10.0, 0.0, -10.0}, + }, + { + name: "All equal", + logits: []float32{5.0, 5.0, 5.0, 5.0, 5.0}, + }, + { + name: "Very large values", + logits: []float32{500.0, 499.0, 498.0}, + }, + { + name: "Very small values", + logits: []float32{-500.0, -499.0, -498.0}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Calculate logprobs for all tokens + var totalProb float64 + for i := range tt.logits { + result := CalculateLogprobs(tt.logits, i, 0, decoder) + if len(result) != 1 { + t.Fatalf("Expected 1 result, got %d", len(result)) + } + prob := math.Exp(result[0].Logprob) + totalProb += prob + + // Verify each probability is between 0 and 1 + if prob < 0 || prob > 1 { + t.Errorf("Token %d probability %f is out of range [0, 1]", i, prob) + } + } + + // Total probability should be very close to 1.0 (allowing for floating point errors) + if math.Abs(totalProb-1.0) > 1e-5 { + t.Errorf("Total probability sum is %f, expected 1.0", totalProb) + } + }) + } +} + +func TestCalculateLogprobsSelectedTokenCorrectness(t *testing.T) { + decoder := func(tokenID int) string { + return string(rune('A' + tokenID)) + } + + logits := []float32{3.0, 1.0, 2.0, 0.5} + + // Test that selecting different tokens gives the correct probabilities + // and that the highest logit has the highest probability + maxLogitIndex := 0 + maxLogitValue := logits[0] + for i, logit := range logits[1:] { + if logit > maxLogitValue { + maxLogitValue = logit + maxLogitIndex = i + 1 + } + } + + var maxProb float64 + var maxProbIndex int + + for i := range logits { + result := CalculateLogprobs(logits, i, 0, decoder) + prob := math.Exp(result[0].Logprob) + + if prob > maxProb { + maxProb = prob + maxProbIndex = i + } + + // Verify the token matches + expectedToken := decoder(i) + if result[0].Token != expectedToken { + t.Errorf("Token %d: expected token %q, got %q", i, expectedToken, result[0].Token) + } + } + + // The token with the highest logit should have the highest probability + if maxProbIndex != maxLogitIndex { + t.Errorf("Token with highest probability (%d) doesn't match token with highest logit (%d)", + maxProbIndex, maxLogitIndex) + } +} + +func TestCalculateLogprobsTopKOrdering(t *testing.T) { + tokens := map[int]string{ + 0: "first", + 1: "second", + 2: "third", + 3: "fourth", + 4: "fifth", + } + decoder := func(tokenID int) string { + return tokens[tokenID] + } + + // Logits in non-sorted order + logits := []float32{2.0, 5.0, 1.0, 4.0, 3.0} + // Expected order by probability: 1 (5.0), 3 (4.0), 4 (3.0), 0 (2.0), 2 (1.0) + expectedOrder := []string{"second", "fourth", "fifth", "first", "third"} + + result := CalculateLogprobs(logits, 0, 5, decoder) + + if len(result) != 1 { + t.Fatalf("Expected 1 result, got %d", len(result)) + } + + if len(result[0].TopLogprobs) != 5 { + t.Fatalf("Expected 5 top logprobs, got %d", len(result[0].TopLogprobs)) + } + + // Verify ordering matches expected + for i, tlp := range result[0].TopLogprobs { + if tlp.Token != expectedOrder[i] { + t.Errorf("Position %d: expected token %q, got %q", i, expectedOrder[i], tlp.Token) + } + } + + // Verify probabilities are in descending order + for i := 1; i < len(result[0].TopLogprobs); i++ { + if result[0].TopLogprobs[i].Logprob > result[0].TopLogprobs[i-1].Logprob { + t.Errorf("Probabilities not in descending order at position %d: %f > %f", + i, result[0].TopLogprobs[i].Logprob, result[0].TopLogprobs[i-1].Logprob) + } + } +} + +func TestLogprobsWithStopSequences(t *testing.T) { + tests := []struct { + name string + pendingResponses []string + pendingLogprobs []llm.Logprob + stop string + expectedResponses []string + expectedLogprobs int + }{ + { + name: "Single token stop", + pendingResponses: []string{"Hello", " world", "!"}, + pendingLogprobs: []llm.Logprob{ + {TokenLogprob: llm.TokenLogprob{Token: "Hello", Logprob: -0.1}}, + {TokenLogprob: llm.TokenLogprob{Token: " world", Logprob: -0.2}}, + {TokenLogprob: llm.TokenLogprob{Token: "!", Logprob: -0.3}}, + }, + stop: "!", + expectedResponses: []string{"Hello", " world"}, + expectedLogprobs: 2, + }, + { + name: "Multi-token stop sequence", + pendingResponses: []string{"Hello", " ", "there", "STOP"}, + pendingLogprobs: []llm.Logprob{ + {TokenLogprob: llm.TokenLogprob{Token: "Hello", Logprob: -0.1}}, + {TokenLogprob: llm.TokenLogprob{Token: " ", Logprob: -0.2}}, + {TokenLogprob: llm.TokenLogprob{Token: "there", Logprob: -0.3}}, + {TokenLogprob: llm.TokenLogprob{Token: "STOP", Logprob: -0.4}}, + }, + stop: "STOP", + expectedResponses: []string{"Hello", " ", "there"}, + expectedLogprobs: 3, + }, + { + name: "Partial token stop", + pendingResponses: []string{"Hello", " the", "re!"}, + pendingLogprobs: []llm.Logprob{ + {TokenLogprob: llm.TokenLogprob{Token: "Hello", Logprob: -0.1}}, + {TokenLogprob: llm.TokenLogprob{Token: " the", Logprob: -0.2}}, + {TokenLogprob: llm.TokenLogprob{Token: "re!", Logprob: -0.3}}, + }, + stop: "there!", + expectedResponses: []string{"Hello", " "}, + expectedLogprobs: 2, + }, + { + name: "Stop at beginning of last token", + pendingResponses: []string{"Hello", " world", "END"}, + pendingLogprobs: []llm.Logprob{ + {TokenLogprob: llm.TokenLogprob{Token: "Hello", Logprob: -0.1}}, + {TokenLogprob: llm.TokenLogprob{Token: " world", Logprob: -0.2}}, + {TokenLogprob: llm.TokenLogprob{Token: "END", Logprob: -0.3}}, + }, + stop: "END", + expectedResponses: []string{"Hello", " world"}, + expectedLogprobs: 2, + }, + { + name: "Multi-token stop across tokens", + pendingResponses: []string{"Text", " ", "with", " ", "stop", " ", "word"}, + pendingLogprobs: []llm.Logprob{ + {TokenLogprob: llm.TokenLogprob{Token: "Text", Logprob: -0.1}}, + {TokenLogprob: llm.TokenLogprob{Token: " ", Logprob: -0.2}}, + {TokenLogprob: llm.TokenLogprob{Token: "with", Logprob: -0.3}}, + {TokenLogprob: llm.TokenLogprob{Token: " ", Logprob: -0.4}}, + {TokenLogprob: llm.TokenLogprob{Token: "stop", Logprob: -0.5}}, + {TokenLogprob: llm.TokenLogprob{Token: " ", Logprob: -0.6}}, + {TokenLogprob: llm.TokenLogprob{Token: "word", Logprob: -0.7}}, + }, + stop: "stop word", + expectedResponses: []string{"Text", " ", "with", " "}, + expectedLogprobs: 4, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Simulate the stop sequence detection and truncation + origLen := len(tt.pendingResponses) + responses, tokenTruncated := TruncateStop(tt.pendingResponses, tt.stop) + newLen := len(responses) + + // Simulate logprobs truncation + logprobs := make([]llm.Logprob, len(tt.pendingLogprobs)) + copy(logprobs, tt.pendingLogprobs) + + origLogprobsLen := len(logprobs) + numTokensRemoved := origLen - newLen + newLogprobsLen := origLogprobsLen - numTokensRemoved + if newLogprobsLen < 0 { + newLogprobsLen = 0 + } + logprobs = logprobs[:newLogprobsLen] + + // Verify responses were truncated correctly + if len(responses) != len(tt.expectedResponses) { + t.Errorf("Expected %d responses, got %d", len(tt.expectedResponses), len(responses)) + } + + // Verify logprobs count matches truncated responses + if len(logprobs) != tt.expectedLogprobs { + t.Errorf("Expected %d logprobs after truncation, got %d", tt.expectedLogprobs, len(logprobs)) + } + + // Verify logprobs count matches response count + if len(logprobs) != len(responses) { + t.Errorf("Logprobs count (%d) doesn't match responses count (%d)", len(logprobs), len(responses)) + } + + // Verify the correct logprobs were kept (skip last token if it was truncated) + // When tokenTruncated is true, the last response token may not match the logprob token + checkLen := len(logprobs) + if tokenTruncated && checkLen > 0 { + checkLen-- // Skip checking the last token when it was partially truncated + } + + for i := range checkLen { + if i < len(responses) && logprobs[i].Token != responses[i] { + t.Errorf("Logprob[%d] token %q doesn't match response[%d] %q", + i, logprobs[i].Token, i, responses[i]) + } + } + }) + } +} diff --git a/runner/llamarunner/runner.go b/runner/llamarunner/runner.go index 87b43256..16c84a78 100644 --- a/runner/llamarunner/runner.go +++ b/runner/llamarunner/runner.go @@ -28,6 +28,12 @@ import ( "github.com/ollama/ollama/runner/common" ) +// response contains a piece of generated text along with optional logprobs +type response struct { + content string + logprobs []llm.Logprob +} + // input is an element of the prompt to process, either // a token or an image embedding (generated from a vision projector) type input struct { @@ -53,11 +59,14 @@ type Sequence struct { // tokens that have been generated but not returned yet (e.g. for stop sequences) pendingResponses []string + // logprobs for tokens that haven't been returned yet + pendingLogprobs []llm.Logprob + // input cache being used by this sequence cache *InputCacheSlot // channel to send responses over - responses chan string + responses chan response // channel to stop decoding (such as if the remote connection is closed) quit chan bool @@ -84,6 +93,10 @@ type Sequence struct { doneReason llm.DoneReason + // logprobs configuration + logprobs bool + topLogprobs int + // Metrics processingDuration time.Duration generationDuration time.Duration @@ -99,6 +112,8 @@ type NewSequenceParams struct { embedding bool shift bool truncate bool + logprobs bool + topLogprobs int } var errorInputTooLong = errors.New("the input length exceeds the context length") @@ -155,7 +170,7 @@ func (s *Server) NewSequence(prompt string, images []llm.ImageData, params NewSe numPromptInputs: len(inputs), numPredict: params.numPredict, pendingResponses: make([]string, 0), - responses: make(chan string, 100), + responses: make(chan response, 100), quit: make(chan bool, 1), embedding: make(chan []float32, 1), samplingCtx: sc, @@ -163,9 +178,16 @@ func (s *Server) NewSequence(prompt string, images []llm.ImageData, params NewSe stop: params.stop, numKeep: params.numKeep, shift: params.shift, + logprobs: params.logprobs, + topLogprobs: params.topLogprobs, }, nil } +// calculateLogprobsLlama converts raw logits to log probabilities and finds top K tokens +func calculateLogprobsLlama(logits []float32, selectedToken int, topK int, model *llama.Model) []llm.Logprob { + return common.CalculateLogprobs(logits, selectedToken, topK, model.TokenToPiece) +} + // inputs processes the prompt and images into a list of inputs // by splitting the prompt on [img-] tags, tokenizing text and // generating image embeddings for each image @@ -294,7 +316,9 @@ func (s *Server) allNil() bool { func flushPending(seq *Sequence) bool { joined := strings.Join(seq.pendingResponses, "") + logprobs := seq.pendingLogprobs seq.pendingResponses = []string{} + seq.pendingLogprobs = []llm.Logprob{} // Check if there are any partial UTF-8 characters remaining. // We already check and queue as we are generating but some may @@ -311,7 +335,7 @@ func flushPending(seq *Sequence) bool { } select { - case seq.responses <- joined: + case seq.responses <- response{content: joined, logprobs: logprobs}: return true case <-seq.quit: return false @@ -526,6 +550,15 @@ func (s *Server) processBatch(tokenBatch *llama.Batch, embedBatch *llama.Batch) continue } + // Calculate logprobs if requested (after EOS check to avoid logprobs for EOS tokens) + if seq.logprobs { + logits := s.lc.GetLogitsIth(seq.iBatch) + if logits != nil { + logprobs := calculateLogprobsLlama(logits, token, seq.topLogprobs, s.model) + seq.pendingLogprobs = append(seq.pendingLogprobs, logprobs...) + } + } + seq.inputs = []input{{token: token}} seq.pendingResponses = append(seq.pendingResponses, piece) @@ -539,6 +572,17 @@ func (s *Server) processBatch(tokenBatch *llama.Batch, embedBatch *llama.Batch) seq.pendingResponses, tokenTruncated = common.TruncateStop(seq.pendingResponses, stop) newLen := len(seq.pendingResponses) + // Truncate logprobs to match the truncated responses + if seq.logprobs { + origLogprobsLen := len(seq.pendingLogprobs) + numTokensRemoved := origLen - newLen + newLogprobsLen := origLogprobsLen - numTokensRemoved + if newLogprobsLen < 0 { + newLogprobsLen = 0 + } + seq.pendingLogprobs = seq.pendingLogprobs[:newLogprobsLen] + } + // Update the cache based on the tokens that will be returned: // - We have 1 token more than is currently in the cache because // the last one generated wasn't submitted to Decode @@ -618,6 +662,8 @@ func (s *Server) completion(w http.ResponseWriter, r *http.Request) { embedding: false, shift: req.Shift, truncate: req.Truncate, + logprobs: req.Logprobs, + topLogprobs: req.TopLogprobs, }) if err != nil { if errors.Is(err, errorInputTooLong) { @@ -669,10 +715,11 @@ func (s *Server) completion(w http.ResponseWriter, r *http.Request) { case <-r.Context().Done(): close(seq.quit) return - case content, ok := <-seq.responses: + case resp, ok := <-seq.responses: if ok { if err := json.NewEncoder(w).Encode(&llm.CompletionResponse{ - Content: content, + Content: resp.content, + Logprobs: resp.logprobs, }); err != nil { http.Error(w, fmt.Sprintf("failed to encode response: %v", err), http.StatusInternalServerError) close(seq.quit) diff --git a/runner/ollamarunner/runner.go b/runner/ollamarunner/runner.go index 3e8c1e22..15339086 100644 --- a/runner/ollamarunner/runner.go +++ b/runner/ollamarunner/runner.go @@ -41,6 +41,12 @@ import ( _ "github.com/ollama/ollama/model/models" ) +// response contains a piece of generated text along with optional logprobs +type response struct { + content string + logprobs []llm.Logprob +} + type Sequence struct { // ctxs are used for allocating tensors that last the lifetime of the sequence, such as // multimodal embeddings @@ -61,11 +67,14 @@ type Sequence struct { // tokens that have been generated but not returned yet (e.g. for stop sequences) pendingResponses []string + // logprobs for tokens that haven't been returned yet + pendingLogprobs []llm.Logprob + // input cache being used by this sequence cache *InputCacheSlot // channel to send responses over - responses chan string + responses chan response // channel to stop decoding (such as if the remote connection is closed) quit chan bool @@ -93,6 +102,10 @@ type Sequence struct { doneReason llm.DoneReason + // logprobs configuration + logprobs bool + topLogprobs int + // Metrics startedAt, lastUpdatedAt time.Time processingDuration time.Duration @@ -102,13 +115,15 @@ type Sequence struct { } type NewSequenceParams struct { - numPredict int - stop []string - numKeep int32 - sampler sample.Sampler - embedding bool - shift bool - truncate bool + numPredict int + stop []string + numKeep int32 + sampler sample.Sampler + embedding bool + shift bool + truncate bool + logprobs bool + topLogprobs int } var errorInputTooLong = errors.New("the input length exceeds the context length") @@ -181,7 +196,7 @@ func (s *Server) NewSequence(prompt string, images []llm.ImageData, params NewSe numPromptInputs: len(inputs), numPredict: params.numPredict, pendingResponses: make([]string, 0), - responses: make(chan string, 100), + responses: make(chan response, 100), quit: make(chan bool, 1), embedding: make(chan []float32, 1), sampler: params.sampler, @@ -189,9 +204,20 @@ func (s *Server) NewSequence(prompt string, images []llm.ImageData, params NewSe stop: params.stop, numKeep: params.numKeep, shift: params.shift, + logprobs: params.logprobs, + topLogprobs: params.topLogprobs, }, nil } +// calculateLogprobs converts raw logits to log probabilities and finds top K tokens +func calculateLogprobs(logits []float32, selectedToken int32, topK int, textProcessor model.TextProcessor) []llm.Logprob { + decoder := func(tokenID int) string { + text, _ := textProcessor.Decode([]int32{int32(tokenID)}) + return text + } + return common.CalculateLogprobs(logits, int(selectedToken), topK, decoder) +} + // inputs processes the prompt and images into a list of inputs // by splitting the prompt on [img-] tags, tokenizing text and // decoding images @@ -371,7 +397,9 @@ func (s *Server) allNil() bool { func flushPending(seq *Sequence) bool { joined := strings.Join(seq.pendingResponses, "") + logprobs := seq.pendingLogprobs seq.pendingResponses = []string{} + seq.pendingLogprobs = []llm.Logprob{} // Check if there are any partial UTF-8 characters remaining. // We already check and queue as we are generating but some may @@ -388,7 +416,7 @@ func flushPending(seq *Sequence) bool { } select { - case seq.responses <- joined: + case seq.responses <- response{content: joined, logprobs: logprobs}: return true case <-seq.quit: return false @@ -729,7 +757,8 @@ func (s *Server) computeBatch(activeBatch batchState) { // sample a token vocabSize := len(outputs) / activeBatch.batch.Outputs.Dim(0) logutil.Trace("computeBatch: vocab details", "batchID", activeBatch.id, "seqIdx", i, "len(logits)", len(outputs), "len(activeBatch.batch.Outputs)", activeBatch.batch.Outputs.Dim(0), "vocabSize", vocabSize, "iBatches", iBatches) - token, err := seq.sampler.Sample(outputs[iBatches[i]*vocabSize : (iBatches[i]+1)*vocabSize]) + logits := outputs[iBatches[i]*vocabSize : (iBatches[i]+1)*vocabSize] + token, err := seq.sampler.Sample(logits) if err != nil { panic("failed to sample token") } @@ -751,6 +780,12 @@ func (s *Server) computeBatch(activeBatch batchState) { panic("failed to decode token") } + // Calculate logprobs if requested (after EOS check to avoid logprobs for EOS tokens) + if seq.logprobs { + logprobs := calculateLogprobs(logits, token, seq.topLogprobs, s.model.(model.TextProcessor)) + seq.pendingLogprobs = append(seq.pendingLogprobs, logprobs...) + } + seq.pendingResponses = append(seq.pendingResponses, piece) sequence := strings.Join(seq.pendingResponses, "") @@ -762,6 +797,17 @@ func (s *Server) computeBatch(activeBatch batchState) { seq.pendingResponses, tokenTruncated = common.TruncateStop(seq.pendingResponses, stop) newLen := len(seq.pendingResponses) + // Truncate logprobs to match the truncated responses + if seq.logprobs { + origLogprobsLen := len(seq.pendingLogprobs) + numTokensRemoved := origLen - newLen + newLogprobsLen := origLogprobsLen - numTokensRemoved + if newLogprobsLen < 0 { + newLogprobsLen = 0 + } + seq.pendingLogprobs = seq.pendingLogprobs[:newLogprobsLen] + } + // Update the cache based on the tokens that will be returned: // - We have 1 token more than is currently in the cache because // the last one generated wasn't submitted to Decode @@ -845,13 +891,15 @@ func (s *Server) completion(w http.ResponseWriter, r *http.Request) { ) seq, err := s.NewSequence(req.Prompt, req.Images, NewSequenceParams{ - numPredict: req.Options.NumPredict, - stop: req.Options.Stop, - numKeep: int32(req.Options.NumKeep), - sampler: sampler, - embedding: false, - shift: req.Shift, - truncate: req.Truncate, + numPredict: req.Options.NumPredict, + stop: req.Options.Stop, + numKeep: int32(req.Options.NumKeep), + sampler: sampler, + embedding: false, + shift: req.Shift, + truncate: req.Truncate, + logprobs: req.Logprobs, + topLogprobs: req.TopLogprobs, }) if err != nil { if errors.Is(err, errorInputTooLong) { @@ -903,10 +951,11 @@ func (s *Server) completion(w http.ResponseWriter, r *http.Request) { case <-r.Context().Done(): close(seq.quit) return - case content, ok := <-seq.responses: + case resp, ok := <-seq.responses: if ok { if err := json.NewEncoder(w).Encode(&llm.CompletionResponse{ - Content: content, + Content: resp.content, + Logprobs: resp.logprobs, }); err != nil { http.Error(w, fmt.Sprintf("failed to encode response: %v", err), http.StatusInternalServerError) close(seq.quit) diff --git a/server/logprob.go b/server/logprob.go new file mode 100644 index 00000000..51996c2a --- /dev/null +++ b/server/logprob.go @@ -0,0 +1,29 @@ +package server + +import ( + "github.com/ollama/ollama/api" + "github.com/ollama/ollama/llm" +) + +// toAPILogprobs converts llm.Logprobs to api.Logprobs +func toAPILogprobs(logprobs []llm.Logprob) []api.Logprob { + result := make([]api.Logprob, len(logprobs)) + for i, lp := range logprobs { + result[i] = api.Logprob{ + TokenLogprob: api.TokenLogprob{ + Token: lp.Token, + Logprob: lp.Logprob, + }, + } + if len(lp.TopLogprobs) > 0 { + result[i].TopLogprobs = make([]api.TokenLogprob, len(lp.TopLogprobs)) + for j, tlp := range lp.TopLogprobs { + result[i].TopLogprobs[j] = api.TokenLogprob{ + Token: tlp.Token, + Logprob: tlp.Logprob, + } + } + } + } + return result +} diff --git a/server/routes.go b/server/routes.go index 28d70d62..38ce4e4d 100644 --- a/server/routes.go +++ b/server/routes.go @@ -183,6 +183,11 @@ func (s *Server) GenerateHandler(c *gin.Context) { return } + if req.TopLogprobs < 0 || req.TopLogprobs > 20 { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "top_logprobs must be between 0 and 20"}) + return + } + name := model.ParseName(req.Model) if !name.IsValid() { // Ideally this is "invalid model name" but we're keeping with @@ -212,6 +217,11 @@ func (s *Server) GenerateHandler(c *gin.Context) { return } + if req.TopLogprobs < 0 || req.TopLogprobs > 20 { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "top_logprobs must be between 0 and 20"}) + return + } + if m.Config.RemoteHost != "" && m.Config.RemoteModel != "" { origModel := req.Model @@ -502,12 +512,14 @@ func (s *Server) GenerateHandler(c *gin.Context) { var sb strings.Builder defer close(ch) if err := r.Completion(c.Request.Context(), llm.CompletionRequest{ - Prompt: prompt, - Images: images, - Format: req.Format, - Options: opts, - Shift: req.Shift == nil || *req.Shift, - Truncate: req.Truncate == nil || *req.Truncate, + Prompt: prompt, + Images: images, + Format: req.Format, + Options: opts, + Shift: req.Shift == nil || *req.Shift, + Truncate: req.Truncate == nil || *req.Truncate, + Logprobs: req.Logprobs, + TopLogprobs: req.TopLogprobs, }, func(cr llm.CompletionResponse) { res := api.GenerateResponse{ Model: req.Model, @@ -520,6 +532,7 @@ func (s *Server) GenerateHandler(c *gin.Context) { EvalCount: cr.EvalCount, EvalDuration: cr.EvalDuration, }, + Logprobs: toAPILogprobs(cr.Logprobs), } if builtinParser != nil { @@ -580,6 +593,7 @@ func (s *Server) GenerateHandler(c *gin.Context) { if req.Stream != nil && !*req.Stream { var r api.GenerateResponse + var allLogprobs []api.Logprob var sbThinking strings.Builder var sbContent strings.Builder for rr := range ch { @@ -588,6 +602,10 @@ func (s *Server) GenerateHandler(c *gin.Context) { sbThinking.WriteString(t.Thinking) sbContent.WriteString(t.Response) r = t + // Accumulate logprobs from all chunks for non-streaming response + if len(t.Logprobs) > 0 { + allLogprobs = append(allLogprobs, t.Logprobs...) + } case gin.H: msg, ok := t["error"].(string) if !ok { @@ -609,6 +627,7 @@ func (s *Server) GenerateHandler(c *gin.Context) { r.Thinking = sbThinking.String() r.Response = sbContent.String() + r.Logprobs = allLogprobs c.JSON(http.StatusOK, r) return @@ -1834,6 +1853,11 @@ func (s *Server) ChatHandler(c *gin.Context) { return } + if req.TopLogprobs < 0 || req.TopLogprobs > 20 { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "top_logprobs must be between 0 and 20"}) + return + } + name := model.ParseName(req.Model) if !name.IsValid() { c.JSON(http.StatusBadRequest, gin.H{"error": "model is required"}) @@ -1859,6 +1883,11 @@ func (s *Server) ChatHandler(c *gin.Context) { return } + if req.TopLogprobs < 0 || req.TopLogprobs > 20 { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "top_logprobs must be between 0 and 20"}) + return + } + // expire the runner if len(req.Messages) == 0 && req.KeepAlive != nil && req.KeepAlive.Duration == 0 { s.sched.expireRunner(m) @@ -2104,12 +2133,14 @@ func (s *Server) ChatHandler(c *gin.Context) { // sets up new context given parent context per request ctx, cancel := context.WithCancel(c.Request.Context()) err := r.Completion(ctx, llm.CompletionRequest{ - Prompt: prompt, - Images: images, - Format: currentFormat, - Options: opts, - Shift: req.Shift == nil || *req.Shift, - Truncate: truncate, + Prompt: prompt, + Images: images, + Format: currentFormat, + Options: opts, + Shift: req.Shift == nil || *req.Shift, + Truncate: truncate, + Logprobs: req.Logprobs, + TopLogprobs: req.TopLogprobs, }, func(r llm.CompletionResponse) { res := api.ChatResponse{ Model: req.Model, @@ -2122,7 +2153,9 @@ func (s *Server) ChatHandler(c *gin.Context) { EvalCount: r.EvalCount, EvalDuration: r.EvalDuration, }, + Logprobs: toAPILogprobs(r.Logprobs), } + if r.Done { res.DoneReason = r.DoneReason.String() res.TotalDuration = time.Since(checkpointStart) @@ -2251,6 +2284,7 @@ func (s *Server) ChatHandler(c *gin.Context) { if req.Stream != nil && !*req.Stream { var resp api.ChatResponse var toolCalls []api.ToolCall + var allLogprobs []api.Logprob var sbThinking strings.Builder var sbContent strings.Builder for rr := range ch { @@ -2262,6 +2296,10 @@ func (s *Server) ChatHandler(c *gin.Context) { if len(req.Tools) > 0 { toolCalls = append(toolCalls, t.Message.ToolCalls...) } + // Accumulate logprobs from all chunks for non-streaming response + if len(t.Logprobs) > 0 { + allLogprobs = append(allLogprobs, t.Logprobs...) + } case gin.H: msg, ok := t["error"].(string) if !ok { @@ -2283,6 +2321,7 @@ func (s *Server) ChatHandler(c *gin.Context) { resp.Message.Content = sbContent.String() resp.Message.Thinking = sbThinking.String() + resp.Logprobs = allLogprobs if len(toolCalls) > 0 { resp.Message.ToolCalls = toolCalls diff --git a/server/routes_generate_test.go b/server/routes_generate_test.go index ed5922f2..a6be3bf3 100644 --- a/server/routes_generate_test.go +++ b/server/routes_generate_test.go @@ -1184,6 +1184,86 @@ func TestGenerate(t *testing.T) { }) } +func TestGenerateLogprobs(t *testing.T) { + t.Run("invalid top_logprobs negative", func(t *testing.T) { + gin.SetMode(gin.TestMode) + s := Server{} + w := createRequest(t, s.GenerateHandler, api.GenerateRequest{ + Model: "test", + Prompt: "Hello", + TopLogprobs: -1, + }) + + if w.Code != http.StatusBadRequest { + t.Errorf("expected status 400, got %d", w.Code) + } + + if diff := cmp.Diff(w.Body.String(), `{"error":"top_logprobs must be between 0 and 20"}`); diff != "" { + t.Errorf("mismatch (-got +want):\n%s", diff) + } + }) + + t.Run("invalid top_logprobs too high", func(t *testing.T) { + gin.SetMode(gin.TestMode) + s := Server{} + w := createRequest(t, s.GenerateHandler, api.GenerateRequest{ + Model: "test", + Prompt: "Hello", + TopLogprobs: 21, + }) + + if w.Code != http.StatusBadRequest { + t.Errorf("expected status 400, got %d", w.Code) + } + + if diff := cmp.Diff(w.Body.String(), `{"error":"top_logprobs must be between 0 and 20"}`); diff != "" { + t.Errorf("mismatch (-got +want):\n%s", diff) + } + }) +} + +func TestChatLogprobs(t *testing.T) { + t.Run("invalid top_logprobs negative", func(t *testing.T) { + gin.SetMode(gin.TestMode) + s := Server{} + w := createRequest(t, s.ChatHandler, api.ChatRequest{ + Model: "test", + Messages: []api.Message{ + {Role: "user", Content: "Hello"}, + }, + TopLogprobs: -1, + }) + + if w.Code != http.StatusBadRequest { + t.Errorf("expected status 400, got %d", w.Code) + } + + if diff := cmp.Diff(w.Body.String(), `{"error":"top_logprobs must be between 0 and 20"}`); diff != "" { + t.Errorf("mismatch (-got +want):\n%s", diff) + } + }) + + t.Run("invalid top_logprobs too high", func(t *testing.T) { + gin.SetMode(gin.TestMode) + s := Server{} + w := createRequest(t, s.ChatHandler, api.ChatRequest{ + Model: "test", + Messages: []api.Message{ + {Role: "user", Content: "Hello"}, + }, + TopLogprobs: 21, + }) + + if w.Code != http.StatusBadRequest { + t.Errorf("expected status 400, got %d", w.Code) + } + + if diff := cmp.Diff(w.Body.String(), `{"error":"top_logprobs must be between 0 and 20"}`); diff != "" { + t.Errorf("mismatch (-got +want):\n%s", diff) + } + }) +} + func TestChatWithPromptEndingInThinkTag(t *testing.T) { gin.SetMode(gin.TestMode) From 4372d0bfefc7c705b5765177460605ebc8eef1f3 Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Mon, 10 Nov 2025 14:49:46 -0800 Subject: [PATCH 14/32] llamarunner: Respect device ordering for offloaded layers We used to control the way that llama.cpp saw devices using CUDA_VISIBLE_DEVICES or similar. This would ensure that the layers offloaded to a device were actually the ones intended. This is particularly important because we might reorder devices based on free memory or performance. When we started explicitly scheduling layers, this logic went away but the llamarunner didn't have any way to set the correct order of devices. This meant that the correct number of layers would be assigned to a device but not necessarily the layers that were expected. This change sets up the devices correctly based on the offload information. --- llama/llama.go | 34 +++++++++++++++++++++++++++++----- ml/device.go | 28 ++++++++++++++++++++++++++++ runner/llamarunner/runner.go | 18 ++++++++++++------ 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/llama/llama.go b/llama/llama.go index f8a051ea..582d4128 100644 --- a/llama/llama.go +++ b/llama/llama.go @@ -63,8 +63,13 @@ func BackendInit() { C.llama_backend_init() } -func EnumerateGPUs() []ml.DeviceID { - var ids []ml.DeviceID +type Devices struct { + ml.DeviceID + LlamaID uint64 +} + +func EnumerateGPUs() []Devices { + var ids []Devices for i := range C.ggml_backend_dev_count() { device := C.ggml_backend_dev_get(i) @@ -74,9 +79,12 @@ func EnumerateGPUs() []ml.DeviceID { C.GGML_BACKEND_DEVICE_TYPE_IGPU: var props C.struct_ggml_backend_dev_props C.ggml_backend_dev_get_props(device, &props) - ids = append(ids, ml.DeviceID{ - ID: C.GoString(props.id), - Library: C.GoString(props.library), + ids = append(ids, Devices{ + DeviceID: ml.DeviceID{ + ID: C.GoString(props.id), + Library: C.GoString(props.library), + }, + LlamaID: uint64(i), }) } } @@ -231,6 +239,7 @@ func (c *Context) GetLogitsIth(i int) []float32 { } type ModelParams struct { + Devices []uint64 NumGpuLayers int MainGpu int UseMmap bool @@ -254,6 +263,21 @@ func LoadModelFromFile(modelPath string, params ModelParams) (*Model, error) { cparams.use_mmap = C.bool(params.UseMmap) cparams.vocab_only = C.bool(params.VocabOnly) + var devices []C.ggml_backend_dev_t + for _, llamaID := range params.Devices { + devices = append(devices, C.ggml_backend_dev_get(C.size_t(llamaID))) + } + if len(devices) > 0 { + devices = append(devices, C.ggml_backend_dev_t(C.NULL)) + devicesData := &devices[0] + + var devicesPin runtime.Pinner + devicesPin.Pin(devicesData) + defer devicesPin.Unpin() + + cparams.devices = devicesData + } + if len(params.TensorSplit) > 0 { tensorSplitData := ¶ms.TensorSplit[0] diff --git a/ml/device.go b/ml/device.go index dc91359f..040764fe 100644 --- a/ml/device.go +++ b/ml/device.go @@ -8,6 +8,7 @@ import ( "hash/maphash" "io" "log/slog" + "math" "net/http" "runtime" "slices" @@ -28,6 +29,22 @@ type GPULayers struct { Layers []int } +// FirstLayer returns the smallest layer index scheduled on this GPU, or MaxInt when empty. +func (g GPULayers) FirstLayer() int { + if len(g.Layers) == 0 { + return math.MaxInt + } + + first := g.Layers[0] + for i := 1; i < len(g.Layers); i++ { + if g.Layers[i] < first { + first = g.Layers[i] + } + } + + return first +} + func (g GPULayers) String() string { if len(g.Layers) == 0 { return "" @@ -54,6 +71,17 @@ func (g GPULayers) String() string { // GPULayersList is a set of layer allocations across multiple GPUs type GPULayersList []GPULayers +func (l GPULayersList) Len() int { return len(l) } +func (l GPULayersList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } + +// Sort by the ordering of the layers offloaded +func (l GPULayersList) Less(i, j int) bool { + li := l[i].FirstLayer() + lj := l[j].FirstLayer() + + return li < lj +} + func (l GPULayersList) String() string { if l.Sum() > 0 { return fmt.Sprintf("%v%v", l.Sum(), []GPULayers(l)) diff --git a/runner/llamarunner/runner.go b/runner/llamarunner/runner.go index 16c84a78..a23ddd61 100644 --- a/runner/llamarunner/runner.go +++ b/runner/llamarunner/runner.go @@ -12,6 +12,7 @@ import ( "net/http" "os" "regexp" + "sort" "strconv" "strings" "sync" @@ -900,19 +901,24 @@ func (s *Server) load(w http.ResponseWriter, r *http.Request) { s.seqs = make([]*Sequence, s.parallel) s.seqsSem = semaphore.NewWeighted(int64(s.parallel)) - gpuIDs := llama.EnumerateGPUs() - tensorSplit := make([]float32, len(gpuIDs)) numGPU := 0 - for i := range gpuIDs { - for _, layers := range req.GPULayers { - if gpuIDs[i] == layers.DeviceID { - tensorSplit[i] = float32(len(layers.Layers)) + var tensorSplit []float32 + var llamaIDs []uint64 + + gpuIDs := llama.EnumerateGPUs() + sort.Sort(req.GPULayers) + for _, layers := range req.GPULayers { + for i := range gpuIDs { + if gpuIDs[i].DeviceID == layers.DeviceID { numGPU += len(layers.Layers) + tensorSplit = append(tensorSplit, float32(len(layers.Layers))) + llamaIDs = append(llamaIDs, gpuIDs[i].LlamaID) } } } params := llama.ModelParams{ + Devices: llamaIDs, NumGpuLayers: numGPU, MainGpu: req.MainGPU, UseMmap: req.UseMmap && len(req.LoraPath) == 0, From f560bd077fcf4e30a4bf584b3b1501015105d63b Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Wed, 5 Nov 2025 14:17:09 -0800 Subject: [PATCH 15/32] llm: Use Ollama engine memory layouts for both old and new engines Currently for both the old and new engines, there is code to calculate how much memory is required for a model and lay out the layers onto GPUs. This reuses the new engine's lay out code for the old engine as well, bringing them closer together. The old engine continues to use its current method of estimating required memory. This reduces maintainence effort and improves consistency, as new features only need to be implemented in one place. The newer code is also more accurate, especially with multiple GPUs. --- fs/ggml/ggml.go | 67 ------ llm/memory.go | 516 --------------------------------------------- llm/memory_test.go | 130 ------------ llm/server.go | 369 ++++++++++++++++---------------- server/sched.go | 17 ++ 5 files changed, 210 insertions(+), 889 deletions(-) delete mode 100644 llm/memory.go delete mode 100644 llm/memory_test.go diff --git a/fs/ggml/ggml.go b/fs/ggml/ggml.go index 909104c6..0b5d37a7 100644 --- a/fs/ggml/ggml.go +++ b/fs/ggml/ggml.go @@ -797,73 +797,6 @@ func (f GGML) GraphSize(context, batch uint64, numParallel int, kvCacheType stri return } -func (llm GGML) VisionGraphSize() (weights, graphSize uint64) { - if llm.KV().Uint("vision.block_count") == 0 { - return - } - - for name, layer := range llm.Tensors().GroupLayers() { - if name == "v" || strings.HasPrefix(name, "v.") { - for _, tensor := range layer { - weights += tensor.Size() - } - } - } - - imageSize := uint64(llm.KV().Uint("vision.image_size")) - patchSize := uint64(llm.KV().Uint("vision.patch_size")) - if patchSize == 0 { - slog.Warn("unknown patch size for vision model") - return - } - - numChannels := uint64(llm.KV().Uint("vision.num_channels")) - - numPatches := (imageSize / patchSize) * (imageSize / patchSize) - if _, ok := llm.Tensors().GroupLayers()["v"]["class_embd"]; ok { - numPatches++ - } - - headCount := uint64(llm.KV().Uint("vision.attention.head_count")) - embeddingLength := uint64(llm.KV().Uint("vision.embedding_length")) - - switch llm.KV().Architecture() { - case "mllama": - numPaddedPatches := numPatches + 8 - (numPatches%8)%8 - - maxNumTiles := uint64(llm.KV().Uint("vision.max_num_tiles")) - - graphSize = 4 * (8 + - imageSize*imageSize*numChannels*maxNumTiles + - embeddingLength*numPatches*maxNumTiles + - 9*embeddingLength*numPaddedPatches*maxNumTiles + - numPaddedPatches*maxNumTiles*numPaddedPatches*maxNumTiles*headCount) - case "gemma3", "mistral3": - graphSize = 4 * (imageSize*imageSize*numChannels + - embeddingLength*patchSize + - numPatches*numPatches*headCount) - case "qwen25vl": - maxPixels := uint64(llm.KV().Uint("vision.max_pixels", 28*28*1280)) - - numPatches := maxPixels / (patchSize * patchSize) - - graphSize = 4 * (maxPixels*numChannels + // Original image storage - // Normalized pixels - maxPixels*numChannels + - // Patches storage (numPatches * channels * patchSize^2) - numPatches*numChannels*patchSize*patchSize + - // Self-attention calculations - numPatches*numPatches*headCount + - // Additional buffer for processing - embeddingLength*numPatches) - case "llama4": - // vision graph is computed independently in the same schedule - // and is negligible compared to the worst case text graph - } - - return weights, graphSize -} - // SupportsKVCacheType checks if the requested cache type is supported func (f GGML) SupportsKVCacheType(cacheType string) bool { if cacheType == "" || cacheType == "f16" { diff --git a/llm/memory.go b/llm/memory.go deleted file mode 100644 index 15558109..00000000 --- a/llm/memory.go +++ /dev/null @@ -1,516 +0,0 @@ -package llm - -import ( - "fmt" - "log/slog" - "os" - "slices" - "sort" - "strings" - - "github.com/ollama/ollama/api" - "github.com/ollama/ollama/envconfig" - "github.com/ollama/ollama/format" - "github.com/ollama/ollama/fs/ggml" - "github.com/ollama/ollama/ml" -) - -// pickBestFullFitByLibrary will try to find the optimal placement of the model in the available GPUs where the model fully fits -// The list of GPUs returned will always be the same brand (library) -// If the model can not be fit fully within the available GPU(s) nil is returned -func pickBestFullFitByLibrary(f *ggml.GGML, modelPath string, projectors []string, adapters []string, opts api.Options, gpus []ml.DeviceInfo, numParallel int) []ml.DeviceInfo { - for _, gl := range ml.ByLibrary(gpus) { - sgl := append(make([]ml.DeviceInfo, 0, len(gl)), gl...) - - // TODO - potentially sort by performance capability, existing models loaded, etc. - // TODO - Eliminate any GPUs that already have envconfig.MaxRunners loaded on them - // Note: at present, this will favor most current available VRAM descending and ignoring faster GPU speed in mixed setups - sort.Sort(sort.Reverse(ml.ByFreeMemory(sgl))) - - if !envconfig.SchedSpread() { - // Try to pack into as few GPUs as possible, starting from 1 GPU - for numGPUs := 1; numGPUs <= len(sgl); numGPUs++ { - gpuSubset := sgl[:numGPUs] - ok, estimatedVRAM := predictServerFit(gpuSubset, f, adapters, projectors, opts, numParallel) - - if ok { - slog.Info("new model will fit in available VRAM across minimum required GPUs, loading", - "model", modelPath, - "library", sgl[0].Library, - "parallel", numParallel, - "required", format.HumanBytes2(estimatedVRAM), - "gpus", numGPUs) - return gpuSubset - } - } - } else { - // TODO future refinements - // - if multiple Libraries, see if any single GPU in any Library will fit - // - try subsets of GPUs instead of just falling back to 1 or all in a family - - // Now try all the GPUS (OLLAMA_SCHED_SPREAD is set) - if ok, estimatedVRAM := predictServerFit(sgl, f, adapters, projectors, opts, numParallel); ok { - slog.Info("new model will fit in available VRAM, loading", - "model", modelPath, - "library", sgl[0].Library, - "parallel", numParallel, - "required", format.HumanBytes2(estimatedVRAM), - "gpus", len(sgl)) - return sgl - } - } - } - return nil -} - -// If multiple Libraries are detected, pick the Library which loads the most layers for the model -func pickBestPartialFitByLibrary(f *ggml.GGML, projectors []string, adapters []string, opts api.Options, gpus []ml.DeviceInfo, numParallel int) []ml.DeviceInfo { - byLibrary := ml.ByLibrary(gpus) - if len(byLibrary) <= 1 { - return gpus - } - var bestEstimate uint64 - var bestFit int - for i, gl := range byLibrary { - _, estimatedVRAM := predictServerFit(gl, f, adapters, projectors, opts, numParallel) - if estimatedVRAM > bestEstimate { - bestEstimate = estimatedVRAM - bestFit = i - } - } - return byLibrary[bestFit] -} - -// This algorithm looks for a complete fit to determine if we need to unload other models -func predictServerFit(allGpus []ml.DeviceInfo, f *ggml.GGML, adapters, projectors []string, opts api.Options, numParallel int) (bool, uint64) { - // Split up the GPUs by type and try them - var estimatedVRAM uint64 - for _, gpus := range ml.ByLibrary(allGpus) { - var layerCount int - estimate := estimateGPULayers(gpus, f, projectors, opts, numParallel) - layerCount, estimatedVRAM = estimate.Layers, estimate.VRAMSize - if opts.NumGPU < 0 { - if layerCount > 0 && layerCount >= int(f.KV().BlockCount()+1) { - return true, estimatedVRAM - } - } else { - if layerCount > 0 && layerCount >= opts.NumGPU { - return true, estimatedVRAM - } - } - } - return false, estimatedVRAM -} - -func verifyCPUFit(f *ggml.GGML, modelPath string, projectors []string, adapters []string, opts api.Options, systemInfo ml.SystemInfo, numParallel int) bool { - estimate := estimateGPULayers(nil, f, projectors, opts, numParallel) - if estimate.TotalSize > systemInfo.FreeMemory { - return false - } - slog.Info("new model will fit in available system memory for CPU inference, loading", - "model", modelPath, - "parallel", numParallel, - "required", format.HumanBytes2(estimate.TotalSize), - ) - return true -} - -type MemoryEstimate struct { - // How many layers we predict we can load - Layers int - - // The size of the graph which occupies the main GPU - Graph uint64 - - // How much VRAM will be allocated given the number of layers we predict - VRAMSize uint64 - - // The total size of the model if loaded into VRAM. If all layers are loaded, VRAMSize == TotalSize - TotalSize uint64 - - // For multi-GPU scenarios, this provides the tensor split parameter - TensorSplit []int - - // For multi-GPU scenarios, this is the size in bytes per GPU - GPUSizes []uint64 - - // internal fields for logging purposes - inferenceLibrary string - layersRequested int - layersModel int - availableList []string - kv uint64 - allocationsList []string - memoryWeights uint64 - memoryLayerOutput uint64 - graphFullOffload uint64 - graphPartialOffload uint64 - - projectorWeights, projectorGraph uint64 -} - -// Given a model and one or more GPU targets, predict how many layers and bytes we can load, and the total size -// The GPUs provided must all be the same Library -func estimateGPULayers(gpus []ml.DeviceInfo, f *ggml.GGML, projectors []string, opts api.Options, numParallel int) MemoryEstimate { - // Graph size for a partial offload, applies to all GPUs - var graphPartialOffload uint64 - - // Graph size when all layers are offloaded, applies to all GPUs - var graphFullOffload uint64 - - // Final graph offload once we know full or partial - var graphOffload uint64 - - // Projectors loaded into GPU0 only - var llamaEngineProjectorWeights uint64 - - // Projectors loaded with output layer - var ollamaEngineProjectorWeights uint64 - var ollamaEngineProjectorGraph uint64 - - // Conditional output size on GPU 0 - var memoryLayerOutput uint64 - - // The sizes of a layer - var layerSize uint64 - - // The sum of all the layer sizes (just for logging) - var memoryWeights uint64 - - // True if all the layers are loaded - var fullyLoaded bool - - // Overflow that didn't fit into the GPU - var overflow uint64 - - overhead := envconfig.GpuOverhead() - availableList := make([]string, len(gpus)) - libraries := []string{} - for i, gpu := range gpus { - availableList[i] = format.HumanBytes2(gpu.FreeMemory) - if !slices.Contains(libraries, gpu.Library) { - libraries = append(libraries, gpu.Library) - } - } - if len(libraries) == 0 { - libraries = []string{"cpu"} - } - slog.Debug("evaluating", "library", strings.Join(libraries, ","), "gpu_count", len(gpus), "available", availableList) - - for _, projector := range projectors { - llamaEngineProjectorWeights += projectorMemoryRequirements(projector) - } - if llamaEngineProjectorWeights == 0 { - ollamaEngineProjectorWeights, ollamaEngineProjectorGraph = f.VisionGraphSize() - } - - layers := f.Tensors().GroupLayers() - // add one layer worth of memory as a buffer - if blk0, ok := layers["blk.0"]; ok { - layerSize = blk0.Size() - } else { - slog.Warn("model missing blk.0 layer size") - } - - useFlashAttention := envconfig.FlashAttention(f.FlashAttention()) && - ml.FlashAttentionSupported(gpus) && - f.SupportsFlashAttention() - - var kvct string - if useFlashAttention { - requested := strings.ToLower(envconfig.KvCacheType()) - if f.SupportsKVCacheType(requested) { - kvct = requested - } - } - - kv, graphPartialOffload, graphFullOffload := f.GraphSize(uint64(opts.NumCtx), uint64(min(opts.NumCtx, opts.NumBatch)), numParallel, kvct, useFlashAttention) - - if len(kv) > 0 { - layerSize += kv[0] - } - - var kvTotal uint64 - for _, kvLayer := range kv { - kvTotal += kvLayer - } - - if graphPartialOffload == 0 { - headsKV := f.KV().HeadCountKVMin() - if headsKV == 0 { - headsKV = 1 - } - gqa := f.KV().HeadCountMax() / headsKV - graphPartialOffload = gqa * kvTotal / 6 - } - if graphFullOffload == 0 { - graphFullOffload = graphPartialOffload - } - - // on metal there's no partial offload overhead - if len(gpus) > 0 && gpus[0].Library == "Metal" { - graphPartialOffload = graphFullOffload - } else if len(gpus) > 1 { - // multigpu should always use the partial graph size - graphFullOffload = graphPartialOffload - } - - // Output layer handled at the end if we have space - if layer, ok := layers["output_norm"]; ok { - memoryLayerOutput += layer.Size() - } - if layer, ok := layers["output"]; ok { - memoryLayerOutput += layer.Size() - } else if layer, ok := layers["token_embd"]; ok { - memoryLayerOutput += layer.Size() - } - - gpuZeroOverhead := llamaEngineProjectorWeights - - // Reduce set of GPUs to only those that have sufficient space to fit overhead and at least one layer - var layerCount int - tensorSplit := make([]int, len(gpus)) - gpuAllocations := make([]uint64, len(gpus)) - type gs struct { - i int - g *ml.DeviceInfo - } - gpusWithSpace := []gs{} - for i := range gpus { - var gzo uint64 - if len(gpusWithSpace) == 0 { - gzo = gpuZeroOverhead - } - // Only include GPUs that can fit the graph, gpu minimum, the layer buffer and at least more layer - if gpus[i].FreeMemory < overhead+gzo+max(graphPartialOffload, graphFullOffload)+gpus[i].MinimumMemory()+2*layerSize { - slog.Debug("gpu has too little memory to allocate any layers", - "id", gpus[i].ID, - "library", gpus[i].Library, - "compute", gpus[i].Compute(), - "driver", fmt.Sprintf("%d.%d", gpus[i].DriverMajor, gpus[i].DriverMinor), - "name", gpus[i].Name, - "total", format.HumanBytes2(gpus[i].TotalMemory), - "available", format.HumanBytes2(gpus[i].FreeMemory), - "minimum_memory", gpus[i].MinimumMemory, - "layer_size", format.HumanBytes2(layerSize), - "gpu_zer_overhead", format.HumanBytes2(gzo), - "partial_offload", format.HumanBytes2(graphPartialOffload), - "full_offload", format.HumanBytes2(graphFullOffload), - ) - continue - } - gpusWithSpace = append(gpusWithSpace, gs{i, &gpus[i]}) - gpuAllocations[i] += gpus[i].MinimumMemory() + layerSize // We hold off on graph until we know partial vs. full - } - - var gpuZeroID int - if len(gpusWithSpace) > 0 { - gpuZeroID = gpusWithSpace[0].i - gpuAllocations[gpuZeroID] += gpuZeroOverhead - } else { - overflow += gpuZeroOverhead - } - - // For all the layers, find where they can fit on the GPU(s) - for i := int(f.KV().BlockCount()) - 1; i >= 0; i-- { - // Some models have inconsistent layer sizes - if blk, ok := layers[fmt.Sprintf("blk.%d", i)]; ok { - layerSize = blk.Size() - layerSize += kv[i] - memoryWeights += blk.Size() - } - - if opts.NumGPU >= 0 && layerCount >= opts.NumGPU { - // Stop allocating on GPU(s) once we hit the users target NumGPU - overflow += layerSize - continue - } - - // distribute the layers across the GPU(s) that have space - for j := len(gpusWithSpace); j > 0; j-- { - g := gpusWithSpace[i%j] - used := gpuAllocations[g.i] + max(graphPartialOffload, graphFullOffload) - if g.g.FreeMemory > overhead+used+layerSize { - gpuAllocations[g.i] += layerSize - tensorSplit[g.i]++ - layerCount++ - break - } else { - gpusWithSpace = append(gpusWithSpace[:i%j], gpusWithSpace[i%j+1:]...) - } - } - - if len(gpusWithSpace) == 0 { - overflow += layerSize - } - } - if layerCount >= int(f.KV().BlockCount()) { - fullyLoaded = true - } - - // Determine if we need to consider output then find where it fits - memoryLastLayer := memoryLayerOutput + ollamaEngineProjectorWeights + ollamaEngineProjectorGraph - if memoryLastLayer > 0 { - if opts.NumGPU < 0 || layerCount < opts.NumGPU { - for j := len(gpusWithSpace); j > 0; j-- { - g := gpusWithSpace[layerCount%j] - used := gpuAllocations[g.i] + max(graphPartialOffload, graphFullOffload) - if g.g.FreeMemory > overhead+used+memoryLastLayer { - gpuAllocations[g.i] += memoryLastLayer - tensorSplit[g.i]++ - layerCount++ - break - } - } - } - - if layerCount < int(f.KV().BlockCount())+1 { - fullyLoaded = false - overflow += memoryLastLayer - } - } - - // Add the applicable (full or partial) graph allocations - for i := range gpus { - if tensorSplit[i] <= 0 { - continue - } - if fullyLoaded { - gpuAllocations[i] += graphFullOffload - } else { - gpuAllocations[i] += graphPartialOffload - } - } - if fullyLoaded { - graphOffload = graphFullOffload - } else { - graphOffload = graphPartialOffload - } - - // Summaries for the log - var memoryRequiredPartial, memoryRequiredTotal uint64 - for i := range gpuAllocations { - memoryRequiredPartial += gpuAllocations[i] - } - memoryRequiredTotal = memoryRequiredPartial + overflow - - allocationsList := []string{} - for _, a := range gpuAllocations { - allocationsList = append(allocationsList, format.HumanBytes2(a)) - } - - estimate := MemoryEstimate{ - TotalSize: memoryRequiredTotal, - Layers: 0, - Graph: 0, - VRAMSize: 0, - GPUSizes: []uint64{}, - - inferenceLibrary: strings.Join(libraries, ","), - layersRequested: opts.NumGPU, - layersModel: int(f.KV().BlockCount()) + 1, - availableList: availableList, - kv: kvTotal, - allocationsList: allocationsList, - memoryWeights: memoryWeights, - memoryLayerOutput: memoryLayerOutput, - graphFullOffload: graphFullOffload, - graphPartialOffload: graphPartialOffload, - projectorWeights: llamaEngineProjectorWeights + ollamaEngineProjectorWeights, - projectorGraph: ollamaEngineProjectorGraph, - } - - if len(gpus) == 0 { - return estimate - } - if layerCount == 0 { - slog.Debug("insufficient VRAM to load any model layers") - return estimate - } - estimate.Layers = layerCount - estimate.Graph = graphOffload - estimate.VRAMSize = memoryRequiredPartial - estimate.TotalSize = memoryRequiredTotal - estimate.TensorSplit = tensorSplit - estimate.GPUSizes = gpuAllocations - return estimate -} - -func (m MemoryEstimate) LogValue() slog.Value { - attrs := []slog.Attr{ - slog.String("library", m.inferenceLibrary), - slog.Group( - "layers", - // requested number of layers to offload - "requested", m.layersRequested, - // The number of layers the model has (including output) - "model", m.layersModel, - // estimated number of layers that can be offloaded - "offload", m.Layers, - // multi-gpu split for tensors - "split", m.TensorSplit, - ), - slog.Group( - "memory", - // memory available by GPU for offloading - "available", m.availableList, - "gpu_overhead", format.HumanBytes2(envconfig.GpuOverhead()), - slog.Group( - "required", - // memory required for full offloading - "full", format.HumanBytes2(m.TotalSize), - // memory required to offload layers.estimate layers - "partial", format.HumanBytes2(m.VRAMSize), - // memory of KV cache - "kv", format.HumanBytes2(m.kv), - // Allocations across the GPUs - "allocations", m.allocationsList, - ), - slog.Group( - "weights", - // memory of the weights - "total", format.HumanBytes2(m.memoryWeights+m.memoryLayerOutput), - // memory of repeating layers - "repeating", format.HumanBytes2(m.memoryWeights), - // memory of non-repeating layers - "nonrepeating", format.HumanBytes2(m.memoryLayerOutput), - ), - slog.Group( - "graph", - // memory of graph when fully offloaded - "full", format.HumanBytes2(m.graphFullOffload), - // memory of graph when not fully offloaded - "partial", format.HumanBytes2(m.graphPartialOffload), - ), - ), - } - - if m.projectorWeights > 0 { - attrs = append(attrs, slog.Group( - "projector", - "weights", format.HumanBytes2(m.projectorWeights), - "graph", format.HumanBytes2(m.projectorGraph), - )) - } - - return slog.GroupValue(attrs...) -} - -func projectorMemoryRequirements(filename string) (weights uint64) { - file, err := os.Open(filename) - if err != nil { - return 0 - } - defer file.Close() - - ggml, err := ggml.Decode(file, 1024) - if err != nil { - return 0 - } - - for _, layer := range ggml.Tensors().GroupLayers() { - weights += layer.Size() - } - - return weights -} diff --git a/llm/memory_test.go b/llm/memory_test.go deleted file mode 100644 index fce17b9c..00000000 --- a/llm/memory_test.go +++ /dev/null @@ -1,130 +0,0 @@ -package llm - -import ( - "bytes" - "fmt" - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/ollama/ollama/api" - "github.com/ollama/ollama/format" - "github.com/ollama/ollama/fs/ggml" - "github.com/ollama/ollama/ml" -) - -func TestEstimateGPULayers(t *testing.T) { - t.Setenv("OLLAMA_DEBUG", "1") - t.Setenv("OLLAMA_KV_CACHE_TYPE", "") // Ensure default f16 - t.Setenv("OLLAMA_CONTEXT_LENGTH", "2048") - - modelName := "dummy" - f, err := os.CreateTemp(t.TempDir(), modelName) - require.NoError(t, err) - defer f.Close() - inputLayerCount := 5 - - tensors := []*ggml.Tensor{ - {Name: "blk.0.attn.weight", Kind: uint32(0), Offset: uint64(0), Shape: []uint64{1, 1, 1, 1}, WriterTo: bytes.NewReader(make([]byte, 32))}, - {Name: "blk.1.attn.weight", Kind: uint32(0), Offset: uint64(0), Shape: []uint64{1, 1, 1, 1}, WriterTo: bytes.NewReader(make([]byte, 32))}, - {Name: "blk.2.attn.weight", Kind: uint32(0), Offset: uint64(0), Shape: []uint64{1, 1, 1, 1}, WriterTo: bytes.NewReader(make([]byte, 32))}, - {Name: "blk.3.attn.weight", Kind: uint32(0), Offset: uint64(0), Shape: []uint64{1, 1, 1, 1}, WriterTo: bytes.NewReader(make([]byte, 32))}, - {Name: "blk.4.attn.weight", Kind: uint32(0), Offset: uint64(0), Shape: []uint64{1, 1, 1, 1}, WriterTo: bytes.NewReader(make([]byte, 32))}, - {Name: "output.weight", Kind: uint32(0), Offset: uint64(0), Shape: []uint64{1, 1, 1, 1}, WriterTo: bytes.NewReader(make([]byte, 32))}, - } - assert.Len(t, tensors, inputLayerCount+1) - err = ggml.WriteGGUF(f, ggml.KV{ - "general.architecture": "llama", - "llama.context_length": uint32(32), - "llama.embedding_length": uint32(4096), - "llama.block_count": uint32(inputLayerCount), - "llama.attention.head_count": uint32(32), - "llama.attention.head_count_kv": uint32(32), - "tokenizer.ggml.tokens": []string{" "}, - "tokenizer.ggml.scores": []float32{0}, - "tokenizer.ggml.token_type": []int32{0}, - }, tensors) - require.NoError(t, err) - - ggml, err := LoadModel(f.Name(), 0) - if err != nil { - t.Fatal(err) - } - - // Simple CPU scenario - gpus := []ml.DeviceInfo{} - projectors := []string{} - opts := api.DefaultOptions() - t.Run("cpu", func(t *testing.T) { - estimate := estimateGPULayers(gpus, ggml, projectors, opts, 1) - assert.Equal(t, 0, estimate.Layers) - assert.Equal(t, uint64(0), estimate.Graph) - }) - - // derived from the dummy ggml file above - graphPartialOffload := uint64(202377216) - graphFullOffload := uint64(171968512) - layerSize := uint64(33554436) - projectorSize := uint64(0) - memoryLayerOutput := uint64(4) - - // Dual CUDA scenario with asymmetry - gpuMinimumMemory := uint64(457 * format.MebiByte) - gpus = []ml.DeviceInfo{ - { - DeviceID: ml.DeviceID{ - Library: "CUDA", - }, - }, - { - DeviceID: ml.DeviceID{ - Library: "CUDA", - }, - }, - } - // Nested array: GPU0 layer space, GPU1 layer space, expected gpu0, expected gpu1 - for i, s := range []struct { - layer0, layer1 uint64 - expect0, expect1 int - }{ - {1, 1, 1, 1}, - {2, 1, 2, 1}, - {2, 2, 2, 2}, - {1, 2, 1, 2}, - {3, 3, 3, 3}, - {4, 4, 3, 3}, - {6, 6, 3, 3}, - {0, 3, 0, 3}, - } { - t.Run(fmt.Sprintf("%v", s), func(t *testing.T) { - gpus[0].FreeMemory = 0 - gpus[1].FreeMemory = 0 - gpus[0].FreeMemory += projectorSize - if s.layer0 > 0 { - gpus[0].FreeMemory += memoryLayerOutput - } else { - gpus[1].FreeMemory += memoryLayerOutput - } - gpus[0].FreeMemory += gpuMinimumMemory + layerSize + s.layer0*layerSize + 1 - gpus[1].FreeMemory += gpuMinimumMemory + layerSize + s.layer1*layerSize + 1 - gpus[0].FreeMemory += max(graphFullOffload, graphPartialOffload) - gpus[1].FreeMemory += max(graphFullOffload, graphPartialOffload) - estimate := estimateGPULayers(gpus, ggml, projectors, opts, 1) - assert.Equal(t, s.expect0+s.expect1, estimate.Layers, "scenario %d: %v", i, s) - assert.Equal(t, []int{s.expect0, s.expect1}, estimate.TensorSplit, "scenario %d: %v", i, s) - var layerSums uint64 - for _, b := range estimate.GPUSizes { - layerSums += b - } - if estimate.Layers < inputLayerCount+1 { - assert.Less(t, estimate.VRAMSize, estimate.TotalSize, "scenario %d: %v %+v", i, s, estimate) - assert.Equal(t, estimate.VRAMSize, layerSums, "scenario %d: %v %+v", i, s, estimate) - } else { - assert.Equal(t, estimate.VRAMSize, estimate.TotalSize, "scenario %d: %v %+v", i, s, estimate) - assert.Equal(t, estimate.TotalSize, layerSums, "scenario %d: %v %+v", i, s, estimate) - } - }) - } -} diff --git a/llm/server.go b/llm/server.go index 87f97a01..83690fcd 100644 --- a/llm/server.go +++ b/llm/server.go @@ -92,7 +92,8 @@ type llmServer struct { numParallel int modelPath string - loadRequest LoadRequest // Parameters used to initialize the runner + loadRequest LoadRequest // Parameters used to initialize the runner + mem *ml.BackendMemory // Memory allocations for this model // llamaModel is an instance of the cgo llama.cpp model definition // nil if this server is running the new engine @@ -113,15 +114,11 @@ type llmServer struct { type llamaServer struct { llmServer - ggml *ggml.GGML - gpus []ml.DeviceInfo // The set of GPUs covered by the memory estimate - estimate MemoryEstimate + ggml *ggml.GGML } type ollamaServer struct { llmServer - - mem *ml.BackendMemory } // LoadModel will load a model from disk. The model must be in the GGML format. @@ -463,169 +460,226 @@ type LoadResponse struct { var ErrLoadRequiredFull = errors.New("unable to load full model on GPU") -func (s *llamaServer) Load(ctx context.Context, systemInfo ml.SystemInfo, gpus []ml.DeviceInfo, requireFull bool) ([]ml.DeviceID, error) { - systemTotalMemory := systemInfo.TotalMemory - systemFreeMemory := systemInfo.FreeMemory - systemSwapFreeMemory := systemInfo.FreeSwap - slog.Info("system memory", "total", format.HumanBytes2(systemTotalMemory), "free", format.HumanBytes2(systemFreeMemory), "free_swap", format.HumanBytes2(systemSwapFreeMemory)) +func (s *llamaServer) Load(ctx context.Context, systemInfo ml.SystemInfo, systemGPUs []ml.DeviceInfo, requireFull bool) ([]ml.DeviceID, error) { + slog.Info("loading model", "model layers", s.totalLayers, "requested", s.options.NumGPU) - if len(gpus) == 0 || s.options.NumGPU == 0 { - if !verifyCPUFit(s.ggml, s.modelPath, []string{s.loadRequest.ProjectorPath}, s.loadRequest.LoraPath, s.options, systemInfo, s.numParallel) { - slog.Info("model requires more memory than is currently available, evicting a model to make space", "estimate", s.estimate) - return nil, fmt.Errorf("model requires more system memory than is currently available %w", ErrLoadRequiredFull) + gpus := append(make([]ml.DeviceInfo, 0, len(systemGPUs)), systemGPUs...) + + // Synthesize memory allocation information based on our estimates + s.mem = &ml.BackendMemory{CPU: ml.DeviceMemory{ + Name: "CPU", + Weights: make([]uint64, s.totalLayers), + Cache: make([]uint64, s.totalLayers), + }, GPUs: make([]ml.DeviceMemory, len(gpus))} + + for i := range s.mem.GPUs { + s.mem.GPUs[i].Name = gpus[i].Name + s.mem.GPUs[i].DeviceID = gpus[i].DeviceID + s.mem.GPUs[i].Weights = make([]uint64, s.totalLayers) + s.mem.GPUs[i].Cache = make([]uint64, s.totalLayers) + } + + kv, graphPartialOffload, graphFullOffload := s.ggml.GraphSize(uint64(s.options.NumCtx), uint64(s.loadRequest.BatchSize), + s.loadRequest.Parallel, s.loadRequest.KvCacheType, s.loadRequest.FlashAttention) + + // Use the size of one layer as a buffer + layers := s.ggml.Tensors().GroupLayers() + if blk0, ok := layers["blk.0"]; ok { + for i := range gpus { + gpus[i].FreeMemory -= blk0.Size() + kv[0] } } else { - g := pickBestFullFitByLibrary(s.ggml, s.modelPath, []string{s.loadRequest.ProjectorPath}, s.loadRequest.LoraPath, s.options, gpus, s.numParallel) - if g == nil { - if !requireFull { - g = pickBestPartialFitByLibrary(s.ggml, []string{s.loadRequest.ProjectorPath}, s.loadRequest.LoraPath, s.options, gpus, s.numParallel) - } else { - slog.Info("model requires more memory than is currently available, evicting a model to make space", "estimate", s.estimate) - return nil, ErrLoadRequiredFull + slog.Warn("model missing blk.0 layer size") + } + + // Assign all the layers to the CPU for now, they will get reassigned later + for i := range s.ggml.KV().BlockCount() { + if blk, ok := layers[fmt.Sprintf("blk.%d", i)]; ok { + s.mem.CPU.Weights[i] = blk.Size() + s.mem.CPU.Cache[i] += kv[i] + } + } + + // We historically haven't included InputWeights in the model size + var outputWeights uint64 + if layer, ok := layers["output_norm"]; ok { + outputWeights += layer.Size() + } + if layer, ok := layers["output"]; ok { + outputWeights += layer.Size() + } else if layer, ok := layers["token_embd"]; ok { + outputWeights += layer.Size() + } + s.mem.CPU.Weights[s.totalLayers-1] = outputWeights + + // The vision projector is always loaded on the first GPU if available. + // This can't be assigned by us, so just subtract it from free space + projectorGPU := -1 + var projectorWeights uint64 + if len(gpus) > 0 { + for _, projector := range s.loadRequest.LoraPath { + projectorWeights += projectorMemoryRequirements(projector) + } + + // llama.cpp uses the first discrete GPU if available, otherwise the first iGPU + firstIntegrated := -1 + for i := range gpus { + if !gpus[i].Integrated { + projectorGPU = i + break + } + if firstIntegrated == -1 { + firstIntegrated = i } } - gpus = g - } - - s.estimate = estimateGPULayers(gpus, s.ggml, []string{s.loadRequest.ProjectorPath}, s.options, s.numParallel) - - if len(gpus) >= 1 { - switch { - case s.options.NumGPU == 0: - gpus = []ml.DeviceInfo{} - case gpus[0].Library == "Metal" && s.estimate.VRAMSize > systemInfo.TotalMemory: - // disable partial offloading when model is greater than total system memory as this - // can lead to locking up the system - s.options.NumGPU = 0 - gpus = []ml.DeviceInfo{} - case gpus[0].Library != "Metal" && s.estimate.Layers == 0: - // Don't bother loading into the GPU if no layers can fit - gpus = []ml.DeviceInfo{} - case s.options.NumGPU < 0 && s.estimate.Layers > 0: - s.options.NumGPU = s.estimate.Layers + if projectorGPU == -1 { + projectorGPU = firstIntegrated } - } else { - s.options.NumGPU = 0 + + gpus[projectorGPU].FreeMemory -= projectorWeights } - // On linux and windows, over-allocating CPU memory will almost always result in an error - // Darwin has fully dynamic swap so has no direct concept of free swap space - if runtime.GOOS != "darwin" { - systemMemoryRequired := s.estimate.TotalSize - s.estimate.VRAMSize - available := systemInfo.FreeMemory + systemInfo.FreeSwap - if systemMemoryRequired > available { - slog.Warn("model request too large for system", "requested", format.HumanBytes2(systemMemoryRequired), "available", format.HumanBytes2(available), "total", format.HumanBytes2(systemInfo.TotalMemory), "free", format.HumanBytes2(systemInfo.FreeMemory), "swap", format.HumanBytes2(systemInfo.FreeSwap)) - return nil, fmt.Errorf("model requires more system memory (%s) than is available (%s)", format.HumanBytes2(systemMemoryRequired), format.HumanBytes2(available)) + var kvTotal uint64 + for _, kvLayer := range kv { + kvTotal += kvLayer + } + + if graphPartialOffload == 0 { + headsKV := s.ggml.KV().HeadCountKVMin() + if headsKV == 0 { + headsKV = 1 + } + gqa := s.ggml.KV().HeadCountMax() / headsKV + graphPartialOffload = gqa * kvTotal / 6 + } + if graphFullOffload == 0 { + graphFullOffload = graphPartialOffload + } + + // On Metal there's no partial offload overhead + if len(gpus) > 0 && gpus[0].Library == "Metal" { + graphPartialOffload = graphFullOffload + } + + // Create a layout based on the memory data that we've built. The compute graph + // for GPUs is iteratively assigned based on the number of GPUs that are required. + var gpuLayers ml.GPULayersList + for { + prevGPULayers := gpuLayers + + var err error + gpuLayers, err = s.createLayout(systemInfo, gpus, s.mem, requireFull, 0) + if err != nil { + return nil, err + } + + if len(gpuLayers) > len(prevGPULayers) { + for _, gl := range gpuLayers { + for i := range s.mem.GPUs { + if gl.DeviceID == s.mem.GPUs[i].DeviceID { + s.mem.GPUs[i].Graph = max(graphPartialOffload, graphFullOffload) + break + } + } + } + } else { + break } } - slog.Info("offload", "", s.estimate) + // This maintains the historical assignment of graph sizes, though it isn't fully accurate + graphSize := graphFullOffload + if gpuLayers.Sum() < int(s.totalLayers) { + graphSize = graphPartialOffload + } - s.gpus = gpus - s.loadRequest.GPULayers = createGPULayers(s.estimate, s.ggml, gpus, s.options.NumGPU) + // For all layers that we have assigned to GPUs, move them in the memory data so + // that it is reported accurately + for _, gl := range gpuLayers { + for i := range s.mem.GPUs { + if gl.DeviceID == s.mem.GPUs[i].DeviceID { + for _, l := range gl.Layers { + s.mem.GPUs[i].Weights[l] = s.mem.CPU.Weights[l] + s.mem.GPUs[i].Cache[l] = s.mem.CPU.Cache[l] - // Mmap is only supported on the llama engine - if s.textProcessor == nil { - s.loadRequest.UseMmap = true + s.mem.CPU.Weights[l] = 0 + s.mem.CPU.Cache[l] = 0 + } - // mmap has issues with partial offloading on metal - for _, g := range gpus { - if g.Library == "Metal" && - uint64(s.options.NumGPU) > 0 && - uint64(s.options.NumGPU) < s.ggml.KV().BlockCount()+1 { - s.options.UseMMap = new(bool) - *s.options.UseMMap = false + s.mem.GPUs[i].Graph = graphSize + break } } + } - // Windows CUDA should not use mmap for best performance - // Linux with a model larger than free space, mmap leads to thrashing - // For CPU loads we want the memory to be allocated, not FS cache - if (runtime.GOOS == "windows" && len(gpus) > 0 && gpus[0].Library == "CUDA" && s.options.UseMMap == nil) || - (runtime.GOOS == "linux" && systemInfo.FreeMemory < s.estimate.TotalSize && s.options.UseMMap == nil) || - (len(gpus) == 0 && s.options.UseMMap == nil) || - (len(gpus) > 0 && gpus[0].Library == "Vulkan" && s.options.UseMMap == nil) || - (s.options.UseMMap != nil && !*s.options.UseMMap) { - s.loadRequest.UseMmap = false + if projectorGPU > 0 && len(s.mem.GPUs[projectorGPU].Weights) > 0 { + s.mem.GPUs[projectorGPU].Weights[s.totalLayers-1] += projectorWeights + } + + slog.Debug("memory", "estimate", s.mem) + s.mem.Log(slog.LevelInfo) + + // The llama engine uses mmap by default + s.loadRequest.UseMmap = true + + // mmap has issues with partial offloading on metal + for _, g := range gpus { + if g.Library == "Metal" && + uint64(s.options.NumGPU) > 0 && + uint64(s.options.NumGPU) < s.totalLayers { + s.options.UseMMap = new(bool) + *s.options.UseMMap = false } } + // Windows CUDA should not use mmap for best performance + // Linux with a model larger than free space, mmap leads to thrashing + // For CPU loads we want the memory to be allocated, not FS cache + if (runtime.GOOS == "windows" && len(gpus) > 0 && gpus[0].Library == "CUDA" && s.options.UseMMap == nil) || + (runtime.GOOS == "linux" && systemInfo.FreeMemory < s.TotalSize() && s.options.UseMMap == nil) || + (len(gpus) == 0 && s.options.UseMMap == nil) || + (len(gpus) > 0 && gpus[0].Library == "Vulkan" && s.options.UseMMap == nil) || + (s.options.UseMMap != nil && !*s.options.UseMMap) { + s.loadRequest.UseMmap = false + } + if err := s.waitUntilRunnerLaunched(ctx); err != nil { return nil, err } + s.loadRequest.GPULayers = gpuLayers resp, err := s.initModel(ctx, s.loadRequest, LoadOperationCommit) if err != nil { return nil, err } - // On the Ollama engine, we can print out a summary of the memory allocations. - // We don't have this for the llama engine but it does something similar itself. - if s.textProcessor != nil { - resp.Memory.Log(slog.LevelInfo) - } - if !resp.Success { - slog.Warn("failed to allocate memory for model", "memory", resp.Memory) return nil, errors.New("failed to allocate memory for model") } // The llama engine does its memory allocations together with model loading, so we // need to wait until it is done to ensure that we have accurate memory data before // loading the next model - if s.textProcessor == nil { - return uniqueDeviceIDs(s.loadRequest.GPULayers), s.WaitUntilRunning(ctx) - } else { - return uniqueDeviceIDs(s.loadRequest.GPULayers), nil - } + return uniqueDeviceIDs(s.loadRequest.GPULayers), s.WaitUntilRunning(ctx) } -// createGPULayers maps from the tensor splits assigned by the memory estimates to explicit assignment -// of particular layers onto GPUs -func createGPULayers(estimate MemoryEstimate, ggml *ggml.GGML, gpus []ml.DeviceInfo, numGPU int) ml.GPULayersList { - if numGPU <= 0 || len(gpus) == 0 { - return nil +func projectorMemoryRequirements(filename string) (weights uint64) { + file, err := os.Open(filename) + if err != nil { + return 0 + } + defer file.Close() + + ggml, err := ggml.Decode(file, 1024) + if err != nil { + return 0 } - gpuLayers := make(ml.GPULayersList, len(gpus)) - for i := range gpuLayers { - gpuLayers[i].DeviceID = gpus[i].DeviceID + for _, layer := range ggml.Tensors().GroupLayers() { + weights += layer.Size() } - var sum float32 - splits := make([]float32, len(estimate.TensorSplit)) - // cumulative sum of all splits - for i := range splits { - sum += float32(estimate.TensorSplit[i]) - splits[i] = sum - } - - if sum <= 0 { - return nil - } - - // normalize splits - for i := range splits { - splits[i] /= sum - } - - blocks := int(ggml.KV().BlockCount()) - gpuRangeStart := max(0, blocks-numGPU) - gpuRangeStop := min(gpuRangeStart+numGPU, blocks+1) - for i := range blocks + 1 { - if i < gpuRangeStart || i >= gpuRangeStop { - continue - } - - index := slices.IndexFunc(splits, func(f float32) bool { return float32(i-gpuRangeStart)/float32(gpuRangeStop-gpuRangeStart) < f }) - if index < 0 || index >= len(gpus) { - continue - } - - gpuLayers[index].Layers = append(gpuLayers[index].Layers, i) - } - - return gpuLayers + return weights } // Load finds the optimal layout of layers to offload on GPUs based on no initial information about the size of the model @@ -652,23 +706,6 @@ func (s *ollamaServer) Load(ctx context.Context, systemInfo ml.SystemInfo, gpus slog.Info("loading model", "model layers", s.totalLayers, "requested", s.options.NumGPU) - systemTotalMemory := systemInfo.TotalMemory - systemFreeMemory := systemInfo.FreeMemory - systemSwapFreeMemory := systemInfo.FreeSwap - slog.Info("system memory", "total", format.HumanBytes2(systemTotalMemory), "free", format.HumanBytes2(systemFreeMemory), "free_swap", format.HumanBytes2(systemSwapFreeMemory)) - - for _, gpu := range gpus { - available := gpu.FreeMemory - envconfig.GpuOverhead() - gpu.MinimumMemory() - if gpu.FreeMemory < envconfig.GpuOverhead()+gpu.MinimumMemory() { - available = 0 - } - slog.Info("gpu memory", "id", gpu.ID, "library", gpu.Library, - "available", format.HumanBytes2(available), - "free", format.HumanBytes2(gpu.FreeMemory), - "minimum", format.HumanBytes2(gpu.MinimumMemory()), - "overhead", format.HumanBytes2(envconfig.GpuOverhead())) - } - pastAllocations := make(map[uint64]struct{}) var backoff float32 @@ -834,25 +871,22 @@ func uniqueDeviceIDs(gpuLayers ml.GPULayersList) []ml.DeviceID { // - Calculating how much space each GPU has available for layers, based on free memory and space occupied by the graph // - Assigning layers // - Ensuring that we don't exceed limits, such as requirements about partial offloading or system memory -func (s *ollamaServer) createLayout(systemInfo ml.SystemInfo, systemGPUs []ml.DeviceInfo, memory *ml.BackendMemory, requireFull bool, backoff float32) (ml.GPULayersList, error) { +func (s *llmServer) createLayout(systemInfo ml.SystemInfo, systemGPUs []ml.DeviceInfo, memory *ml.BackendMemory, requireFull bool, backoff float32) (ml.GPULayersList, error) { if memory == nil { memory = &ml.BackendMemory{CPU: ml.DeviceMemory{ Weights: make([]uint64, s.totalLayers), Cache: make([]uint64, s.totalLayers), }} } - gpuLayers, layers, err := s.buildLayout(systemGPUs, memory, requireFull, backoff) - if err != nil { - return nil, err - } - err = s.verifyLayout(systemInfo, memory, requireFull, gpuLayers, layers) + gpuLayers, layers := s.buildLayout(systemGPUs, memory, requireFull, backoff) + err := s.verifyLayout(systemInfo, memory, requireFull, gpuLayers, layers) if err != nil { return nil, err } return gpuLayers, nil } -func (s *ollamaServer) buildLayout(systemGPUs []ml.DeviceInfo, memory *ml.BackendMemory, requireFull bool, backoff float32) (ml.GPULayersList, []uint64, error) { +func (s *llmServer) buildLayout(systemGPUs []ml.DeviceInfo, memory *ml.BackendMemory, requireFull bool, backoff float32) (ml.GPULayersList, []uint64) { gpus := append(make([]ml.DeviceInfo, 0, len(systemGPUs)), systemGPUs...) sort.Sort(sort.Reverse(ml.ByFreeMemory(gpus))) @@ -910,11 +944,11 @@ func (s *ollamaServer) buildLayout(systemGPUs []ml.DeviceInfo, memory *ml.Backen gpuLayers = libraryGpuLayers } } - return gpuLayers, layers, nil + return gpuLayers, layers } // verifyLayout ensures that we don't exceed limits, such as requirements about partial offloading or system memory -func (s *ollamaServer) verifyLayout(systemInfo ml.SystemInfo, memory *ml.BackendMemory, requireFull bool, gpuLayers ml.GPULayersList, layers []uint64) error { +func (s *llmServer) verifyLayout(systemInfo ml.SystemInfo, memory *ml.BackendMemory, requireFull bool, gpuLayers ml.GPULayersList, layers []uint64) error { // These sizes will only increase as we go through additional iterations and get additional information. cpuSize := memory.InputWeights + memory.CPU.Graph var vramSize uint64 @@ -942,11 +976,13 @@ nextLayer: if requireFull { if gpuLayers.Sum() < len(layers) && (s.options.NumGPU < 0 || gpuLayers.Sum() < s.options.NumGPU) { + slog.Info("model requires more memory than is currently available, evicting a model to make space", "loaded layers", gpuLayers.Sum()) return ErrLoadRequiredFull } if cpuSize > systemInfo.FreeMemory { - return ErrLoadRequiredFull + slog.Info("model requires more system memory than is currently available, evicting a model to make space", "required", cpuSize, "free", systemInfo.FreeMemory) + return fmt.Errorf("model requires more system memory than is currently available %w", ErrLoadRequiredFull) } } @@ -1734,31 +1770,12 @@ func (s *llmServer) Close() error { return nil } -func (s *llamaServer) VRAMSize() uint64 { - return s.estimate.VRAMSize -} - -func (s *llamaServer) TotalSize() uint64 { - return s.estimate.TotalSize -} - -func (s *llamaServer) VRAMByGPU(id ml.DeviceID) uint64 { - for i, gpu := range s.gpus { - if gpu.DeviceID == id { - if i < len(s.estimate.GPUSizes) { - return s.estimate.GPUSizes[i] - } - } - } - return 0 -} - func (s *llamaServer) GetDeviceInfos(ctx context.Context) []ml.DeviceInfo { slog.Debug("llamarunner free vram reporting not supported") return nil } -func (s *ollamaServer) VRAMSize() uint64 { +func (s *llmServer) VRAMSize() uint64 { if s.mem == nil { return 0 } @@ -1786,7 +1803,7 @@ func (s *ollamaServer) VRAMSize() uint64 { return mem } -func (s *ollamaServer) TotalSize() uint64 { +func (s *llmServer) TotalSize() uint64 { if s.mem == nil { return 0 } @@ -1800,7 +1817,7 @@ func (s *ollamaServer) TotalSize() uint64 { return mem } -func (s *ollamaServer) VRAMByGPU(id ml.DeviceID) uint64 { +func (s *llmServer) VRAMByGPU(id ml.DeviceID) uint64 { if s.mem == nil { return 0 } diff --git a/server/sched.go b/server/sched.go index 5ae42efd..c5bc6692 100644 --- a/server/sched.go +++ b/server/sched.go @@ -437,6 +437,23 @@ func (s *Scheduler) load(req *LlmRequest, f *ggml.GGML, systemInfo ml.SystemInfo s.loadedMu.Unlock() + systemTotalMemory := systemInfo.TotalMemory + systemFreeMemory := systemInfo.FreeMemory + systemSwapFreeMemory := systemInfo.FreeSwap + slog.Info("system memory", "total", format.HumanBytes2(systemTotalMemory), "free", format.HumanBytes2(systemFreeMemory), "free_swap", format.HumanBytes2(systemSwapFreeMemory)) + + for _, gpu := range gpus { + available := gpu.FreeMemory - envconfig.GpuOverhead() - gpu.MinimumMemory() + if gpu.FreeMemory < envconfig.GpuOverhead()+gpu.MinimumMemory() { + available = 0 + } + slog.Info("gpu memory", "id", gpu.ID, "library", gpu.Library, + "available", format.HumanBytes2(available), + "free", format.HumanBytes2(gpu.FreeMemory), + "minimum", format.HumanBytes2(gpu.MinimumMemory()), + "overhead", format.HumanBytes2(envconfig.GpuOverhead())) + } + gpuIDs, err := llama.Load(req.ctx, systemInfo, gpus, requireFull) if err != nil { if errors.Is(err, llm.ErrLoadRequiredFull) { From b13fbad0fe663e1493e8ed4b04ed553f861abba9 Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Thu, 6 Nov 2025 10:31:08 -0800 Subject: [PATCH 16/32] llm: Separate llamaServer and ollamaServer code paths Originally, llamaServer represented old memory estimates, which could be used with either the old or new engine. ollamaServer was used only for the new estimates and new engine. Since these implementations did not map directly to engine, there was engine- specific code in common code paths. Now that new estimates are always used for the new engine, there is a direct mapping between server type and engine. This separates out most of the engine-specific code into the correct implementation to make things easier to understand. --- llm/server.go | 114 ++++++++++++++++++++++---------------------------- 1 file changed, 50 insertions(+), 64 deletions(-) diff --git a/llm/server.go b/llm/server.go index 83690fcd..c4b84950 100644 --- a/llm/server.go +++ b/llm/server.go @@ -84,13 +84,12 @@ type LlamaServer interface { // llmServer is an instance of a runner hosting a single model type llmServer struct { - port int - cmd *exec.Cmd - done chan error // Channel to signal when the process exits - status *StatusWriter - options api.Options - numParallel int - modelPath string + port int + cmd *exec.Cmd + done chan error // Channel to signal when the process exits + status *StatusWriter + options api.Options + modelPath string loadRequest LoadRequest // Parameters used to initialize the runner mem *ml.BackendMemory // Memory allocations for this model @@ -100,10 +99,6 @@ type llmServer struct { llamaModel *llama.Model llamaModelLock *sync.Mutex - // textProcessor handles text encoding/decoding for the model in the Ollama engine - // nil if this server is running the llama.cpp based engine - textProcessor model.TextProcessor - totalLayers uint64 loadStart time.Time // Record how long it took the model to load loadProgress float32 @@ -119,6 +114,8 @@ type llamaServer struct { type ollamaServer struct { llmServer + + textProcessor model.TextProcessor // textProcessor handles text encoding/decoding } // LoadModel will load a model from disk. The model must be in the GGML format. @@ -242,8 +239,6 @@ func NewLlamaServer(systemInfo ml.SystemInfo, gpus []ml.DeviceInfo, modelPath st loadRequest: loadRequest, llamaModel: llamaModel, llamaModelLock: &sync.Mutex{}, - textProcessor: textProcessor, - numParallel: numParallel, sem: semaphore.NewWeighted(int64(numParallel)), totalLayers: f.KV().BlockCount() + 1, loadStart: time.Now(), @@ -278,7 +273,7 @@ func NewLlamaServer(systemInfo ml.SystemInfo, gpus []ml.DeviceInfo, modelPath st }() if textProcessor != nil { - return &ollamaServer{llmServer: s}, nil + return &ollamaServer{llmServer: s, textProcessor: textProcessor}, nil } else { return &llamaServer{llmServer: s, ggml: f}, nil } @@ -1681,68 +1676,59 @@ func (s *llmServer) Embedding(ctx context.Context, input string) ([]float32, err return e.Embedding, nil } -type TokenizeRequest struct { - Content string `json:"content"` -} - -type TokenizeResponse struct { - Tokens []int `json:"tokens"` -} - -func (s *llmServer) Tokenize(ctx context.Context, content string) ([]int, error) { +func (s *llamaServer) Tokenize(ctx context.Context, content string) ([]int, error) { s.llamaModelLock.Lock() defer s.llamaModelLock.Unlock() - if s.llamaModel != nil { - return s.llamaModel.Tokenize(content, false, true) + if s.llamaModel == nil { + return nil, fmt.Errorf("no tokenizer configured") } - if s.textProcessor != nil { - tokens, err := s.textProcessor.Encode(content, false) - if err != nil { - return nil, err - } - toks := make([]int, len(tokens)) - for i, t := range tokens { - toks[i] = int(t) - } - return toks, nil + + return s.llamaModel.Tokenize(content, false, true) +} + +func (s *ollamaServer) Tokenize(ctx context.Context, content string) ([]int, error) { + tokens, err := s.textProcessor.Encode(content, false) + if err != nil { + return nil, err } - // not reached - return nil, fmt.Errorf("no tokenizer configured") + + toks := make([]int, len(tokens)) + for i, t := range tokens { + toks[i] = int(t) + } + + return toks, nil } -type DetokenizeRequest struct { - Tokens []int `json:"tokens"` -} - -type DetokenizeResponse struct { - Content string `json:"content"` -} - -func (s *llmServer) Detokenize(ctx context.Context, tokens []int) (string, error) { +func (s *llamaServer) Detokenize(ctx context.Context, tokens []int) (string, error) { s.llamaModelLock.Lock() defer s.llamaModelLock.Unlock() - if s.llamaModel != nil { - var resp string - for _, token := range tokens { - resp += s.llamaModel.TokenToPiece(token) - } - return resp, nil + if s.llamaModel == nil { + return "", fmt.Errorf("no tokenizer configured") } - if s.textProcessor != nil { - toks := make([]int32, len(tokens)) - for i, t := range tokens { - toks[i] = int32(t) - } - content, err := s.textProcessor.Decode(toks) - if err != nil { - return "", err - } - return content, nil + + var resp string + for _, token := range tokens { + resp += s.llamaModel.TokenToPiece(token) } - // not reached - return "", fmt.Errorf("no tokenizer configured") + + return resp, nil +} + +func (s *ollamaServer) Detokenize(ctx context.Context, tokens []int) (string, error) { + toks := make([]int32, len(tokens)) + for i, t := range tokens { + toks[i] = int32(t) + } + + content, err := s.textProcessor.Decode(toks) + if err != nil { + return "", err + } + + return content, nil } func (s *llmServer) Close() error { From 8bf38552def7cb2dcc9504035a8d5f5919f04da0 Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Tue, 4 Nov 2025 11:46:11 -0800 Subject: [PATCH 17/32] llm: Prefer dedicated GPUs over iGPUs when allocating memory We currently assign model layers to GPUs according to free VRAM, which assumes that GPU performance is roughly equal. This does not work well for mixed dGPU and iGPU systems because iGPUs typically use system memory which is large but their performance is slow. This instead assigns layers to dGPUs first and then iGPUs. In the future, this could be generalized to have a more fine grained notion of GPU performance but dGPU vs. iGPU performance is the most extreme. --- llm/server.go | 58 ++++++++++++++++++------------ llm/server_test.go | 89 +++++++++++++++++++++++++++++++--------------- ml/device.go | 22 ++++++++++++ 3 files changed, 117 insertions(+), 52 deletions(-) diff --git a/llm/server.go b/llm/server.go index c4b84950..4eaa88df 100644 --- a/llm/server.go +++ b/llm/server.go @@ -1007,6 +1007,13 @@ nextLayer: // assignLayers packs the maximum number of layers onto the smallest set of GPUs and comes up with a layer assignment func assignLayers(layers []uint64, gpus []ml.DeviceInfo, requireFull bool, requestedLayers int, lastUsedGPU int) (gpuLayers ml.GPULayersList) { + // If the user is manually overriding parameters, treat all GPUs equally so they split according to VRAM + if requestedLayers >= 0 || envconfig.SchedSpread() { + for i := range gpus { + gpus[i].Integrated = false + } + } + // If we can't fit everything then prefer offloading layers other than the output layer for range 2 { // requestedLayers may be -1 if nothing was requested @@ -1039,33 +1046,38 @@ func assignLayers(layers []uint64, gpus []ml.DeviceInfo, requireFull bool, reque // findBestFit binary searches to find the smallest capacity factor that can fit // the max number of layers. The capacity factor is multiplied by the free space on -// each GPU and a small one will force even balancing. +// each GPU and a small one will force even balancing. Higher performance GPUs are +// used first. func findBestFit(layers []uint64, gpus []ml.DeviceInfo, requestedLayers int, forceRequest bool) (gpuLayers ml.GPULayersList) { - var high float32 = 1 - var low float32 = 0 + for _, gl := range ml.ByPerformance(gpus) { + var high float32 = 1 + var low float32 = 0 - // If we need to fulfill the requested number of layers, pretend we have almost infinite VRAM - if requestedLayers >= 0 && forceRequest { - high = 1000 - } - - bestAssignments := greedyFit(layers, gpus, high, requestedLayers) - maxNumGPU := bestAssignments.Sum() - if maxNumGPU == 0 { - return bestAssignments - } - - for high-low > 1e-6 { - mid := (low + high) / 2 - assignments := greedyFit(layers, gpus, mid, requestedLayers) - if assignments.Sum() == maxNumGPU { - high = mid - bestAssignments = assignments - } else { - low = mid + // If we need to fulfill the requested number of layers, pretend we have almost infinite VRAM + if requestedLayers >= 0 && forceRequest { + high = 1000 } + + bestAssignments := greedyFit(layers, gl, high, requestedLayers) + maxNumGPU := bestAssignments.Sum() + + for high-low > 1e-6 { + mid := (low + high) / 2 + assignments := greedyFit(layers, gl, mid, requestedLayers) + if assignments.Sum() == maxNumGPU { + high = mid + bestAssignments = assignments + } else { + low = mid + } + } + + layers = layers[:len(layers)-bestAssignments.Sum()] + requestedLayers -= bestAssignments.Sum() + gpuLayers = append(bestAssignments, gpuLayers...) } - return bestAssignments + + return gpuLayers } // greedyFit assigns layers incrementally to GPUs, spilling over as each runs out of free space diff --git a/llm/server_test.go b/llm/server_test.go index 2d3bf6be..1f5d5cda 100644 --- a/llm/server_test.go +++ b/llm/server_test.go @@ -14,16 +14,11 @@ import ( ) func TestLLMServerFitGPU(t *testing.T) { - type gpu struct { - id ml.DeviceID - free int - } - minMemory := 457 * format.MebiByte tests := []struct { name string - gpus []gpu + gpus []ml.DeviceInfo layers []int numGPU int requireFull bool @@ -38,91 +33,91 @@ func TestLLMServerFitGPU(t *testing.T) { }, { name: "Full single GPU", - gpus: []gpu{{id: ml.DeviceID{ID: "gpu0"}, free: 256*format.MebiByte + minMemory}}, + gpus: []ml.DeviceInfo{{DeviceID: ml.DeviceID{ID: "gpu0"}, FreeMemory: uint64(256*format.MebiByte + minMemory)}}, layers: []int{50 * format.MebiByte, 50 * format.MebiByte, 50 * format.MebiByte}, numGPU: -1, expected: ml.GPULayersList{{DeviceID: ml.DeviceID{ID: "gpu0"}, Layers: []int{0, 1, 2}}}, }, { name: "Partial single GPU", - gpus: []gpu{{id: ml.DeviceID{ID: "gpu0"}, free: 256*format.MebiByte + minMemory}}, + gpus: []ml.DeviceInfo{{DeviceID: ml.DeviceID{ID: "gpu0"}, FreeMemory: uint64(256*format.MebiByte + minMemory)}}, layers: []int{100 * format.MebiByte, 100 * format.MebiByte, 100 * format.MebiByte, 100 * format.MebiByte}, numGPU: -1, expected: ml.GPULayersList{{DeviceID: ml.DeviceID{ID: "gpu0"}, Layers: []int{1, 2}}}, }, { name: "Single GPU with numGPU 1", - gpus: []gpu{{id: ml.DeviceID{ID: "gpu0"}, free: 256*format.MebiByte + minMemory}}, + gpus: []ml.DeviceInfo{{DeviceID: ml.DeviceID{ID: "gpu0"}, FreeMemory: uint64(256*format.MebiByte + minMemory)}}, layers: []int{50 * format.MebiByte, 50 * format.MebiByte, 50 * format.MebiByte}, numGPU: 1, expected: ml.GPULayersList{{DeviceID: ml.DeviceID{ID: "gpu0"}, Layers: []int{1}}}, }, { name: "Single GPU with numGPU 0", - gpus: []gpu{{id: ml.DeviceID{ID: "gpu0"}, free: 256*format.MebiByte + minMemory}}, + gpus: []ml.DeviceInfo{{DeviceID: ml.DeviceID{ID: "gpu0"}, FreeMemory: uint64(256*format.MebiByte + minMemory)}}, layers: []int{50 * format.MebiByte, 50 * format.MebiByte, 50 * format.MebiByte}, numGPU: 0, expected: ml.GPULayersList{}, }, { name: "Single GPU with numGPU 999", - gpus: []gpu{{id: ml.DeviceID{ID: "gpu0"}, free: 256*format.MebiByte + minMemory}}, + gpus: []ml.DeviceInfo{{DeviceID: ml.DeviceID{ID: "gpu0"}, FreeMemory: uint64(256*format.MebiByte + minMemory)}}, layers: []int{100 * format.MebiByte, 100 * format.MebiByte, 100 * format.MebiByte, 100 * format.MebiByte}, numGPU: 999, expected: ml.GPULayersList{{DeviceID: ml.DeviceID{ID: "gpu0"}, Layers: []int{0, 1, 2, 3}}}, }, { name: "Multi GPU fits on one", - gpus: []gpu{{id: ml.DeviceID{ID: "gpu0"}, free: 128*format.MebiByte + minMemory}, {id: ml.DeviceID{ID: "gpu1"}, free: 256*format.MebiByte + minMemory}}, + gpus: []ml.DeviceInfo{{DeviceID: ml.DeviceID{ID: "gpu0"}, FreeMemory: uint64(128*format.MebiByte + minMemory)}, {DeviceID: ml.DeviceID{ID: "gpu1"}, FreeMemory: uint64(256*format.MebiByte + minMemory)}}, layers: []int{50 * format.MebiByte, 50 * format.MebiByte, 50 * format.MebiByte}, numGPU: -1, expected: ml.GPULayersList{{DeviceID: ml.DeviceID{ID: "gpu1"}, Layers: []int{0, 1, 2}}}, }, { name: "Multi GPU split", - gpus: []gpu{{id: ml.DeviceID{ID: "gpu0"}, free: 128*format.MebiByte + minMemory}, {id: ml.DeviceID{ID: "gpu1"}, free: 256*format.MebiByte + minMemory}}, + gpus: []ml.DeviceInfo{{DeviceID: ml.DeviceID{ID: "gpu0"}, FreeMemory: uint64(128*format.MebiByte + minMemory)}, {DeviceID: ml.DeviceID{ID: "gpu1"}, FreeMemory: uint64(256*format.MebiByte + minMemory)}}, layers: []int{256 * format.MebiByte, 50 * format.MebiByte, 50 * format.MebiByte}, numGPU: -1, expected: ml.GPULayersList{{DeviceID: ml.DeviceID{ID: "gpu1"}, Layers: []int{0}}, {DeviceID: ml.DeviceID{ID: "gpu0"}, Layers: []int{1, 2}}}, }, { name: "Multi GPU partial", - gpus: []gpu{{id: ml.DeviceID{ID: "gpu0"}, free: 128*format.MebiByte + minMemory}, {id: ml.DeviceID{ID: "gpu1"}, free: 256*format.MebiByte + minMemory}}, + gpus: []ml.DeviceInfo{{DeviceID: ml.DeviceID{ID: "gpu0"}, FreeMemory: uint64(128*format.MebiByte + minMemory)}, {DeviceID: ml.DeviceID{ID: "gpu1"}, FreeMemory: uint64(256*format.MebiByte + minMemory)}}, layers: []int{256 * format.MebiByte, 256 * format.MebiByte, 50 * format.MebiByte}, numGPU: -1, expected: ml.GPULayersList{{DeviceID: ml.DeviceID{ID: "gpu1"}, Layers: []int{1}}}, }, { name: "Multi GPU numGPU 1", - gpus: []gpu{{id: ml.DeviceID{ID: "gpu0"}, free: 128*format.MebiByte + minMemory}, {id: ml.DeviceID{ID: "gpu1"}, free: 256*format.MebiByte + minMemory}}, + gpus: []ml.DeviceInfo{{DeviceID: ml.DeviceID{ID: "gpu0"}, FreeMemory: uint64(128*format.MebiByte + minMemory)}, {DeviceID: ml.DeviceID{ID: "gpu1"}, FreeMemory: uint64(256*format.MebiByte + minMemory)}}, layers: []int{50 * format.MebiByte, 50 * format.MebiByte, 50 * format.MebiByte}, numGPU: 1, expected: ml.GPULayersList{{DeviceID: ml.DeviceID{ID: "gpu1"}, Layers: []int{1}}}, }, { name: "Multi GPU numGPU 2", - gpus: []gpu{{id: ml.DeviceID{ID: "gpu0"}, free: 128*format.MebiByte + minMemory}, {id: ml.DeviceID{ID: "gpu1"}, free: 256*format.MebiByte + minMemory}}, + gpus: []ml.DeviceInfo{{DeviceID: ml.DeviceID{ID: "gpu0"}, FreeMemory: uint64(128*format.MebiByte + minMemory)}, {DeviceID: ml.DeviceID{ID: "gpu1"}, FreeMemory: uint64(256*format.MebiByte + minMemory)}}, layers: []int{256 * format.MebiByte, 50 * format.MebiByte, 50 * format.MebiByte}, numGPU: 2, expected: ml.GPULayersList{{DeviceID: ml.DeviceID{ID: "gpu1"}, Layers: []int{0}}, {DeviceID: ml.DeviceID{ID: "gpu0"}, Layers: []int{1}}}, }, { name: "Multi GPU numGPU 999", - gpus: []gpu{{id: ml.DeviceID{ID: "gpu0"}, free: 128*format.MebiByte + minMemory}, {id: ml.DeviceID{ID: "gpu1"}, free: 256*format.MebiByte + minMemory}}, + gpus: []ml.DeviceInfo{{DeviceID: ml.DeviceID{ID: "gpu0"}, FreeMemory: uint64(128*format.MebiByte + minMemory)}, {DeviceID: ml.DeviceID{ID: "gpu1"}, FreeMemory: uint64(256*format.MebiByte + minMemory)}}, layers: []int{256 * format.MebiByte, 256 * format.MebiByte, 50 * format.MebiByte}, numGPU: 999, expected: ml.GPULayersList{{DeviceID: ml.DeviceID{ID: "gpu1"}, Layers: []int{0, 1}}, {DeviceID: ml.DeviceID{ID: "gpu0"}, Layers: []int{2}}}, }, { name: "Multi GPU different libraries", - gpus: []gpu{{id: ml.DeviceID{Library: "CUDA", ID: "gpu0"}, free: 128*format.MebiByte + minMemory}, {id: ml.DeviceID{Library: "ROCm", ID: "gpu1"}, free: 256*format.MebiByte + minMemory}}, + gpus: []ml.DeviceInfo{{DeviceID: ml.DeviceID{Library: "CUDA", ID: "gpu0"}, FreeMemory: uint64(128*format.MebiByte + minMemory)}, {DeviceID: ml.DeviceID{Library: "ROCm", ID: "gpu1"}, FreeMemory: uint64(256*format.MebiByte + minMemory)}}, layers: []int{128 * format.MebiByte, 128 * format.MebiByte, 50 * format.MebiByte}, numGPU: -1, expected: ml.GPULayersList{{DeviceID: ml.DeviceID{ID: "gpu1", Library: "ROCm"}, Layers: []int{0, 1}}}, }, { name: "requireFull", - gpus: []gpu{{id: ml.DeviceID{ID: "gpu0"}, free: 256*format.MebiByte + minMemory}}, + gpus: []ml.DeviceInfo{{DeviceID: ml.DeviceID{ID: "gpu0"}, FreeMemory: uint64(256*format.MebiByte + minMemory)}}, layers: []int{100 * format.MebiByte, 100 * format.MebiByte, 100 * format.MebiByte, 100 * format.MebiByte}, numGPU: -1, requireFull: true, @@ -130,12 +125,54 @@ func TestLLMServerFitGPU(t *testing.T) { }, { name: "requireFull numGPU", - gpus: []gpu{{id: ml.DeviceID{ID: "gpu0"}, free: 256 * format.MebiByte}}, + gpus: []ml.DeviceInfo{{DeviceID: ml.DeviceID{ID: "gpu0"}, FreeMemory: uint64(256 * format.MebiByte)}}, layers: []int{100 * format.MebiByte, 100 * format.MebiByte, 100 * format.MebiByte, 100 * format.MebiByte}, numGPU: 4, requireFull: true, expectedErr: ErrLoadRequiredFull, }, + { + name: "iGPU", + gpus: []ml.DeviceInfo{{DeviceID: ml.DeviceID{ID: "gpu0"}, Integrated: true, FreeMemory: uint64(256*format.MebiByte + minMemory)}}, + layers: []int{50 * format.MebiByte, 50 * format.MebiByte, 50 * format.MebiByte}, + numGPU: -1, + expected: ml.GPULayersList{{DeviceID: ml.DeviceID{ID: "gpu0"}, Layers: []int{0, 1, 2}}}, + }, + { + name: "iGPU + dGPU", + gpus: []ml.DeviceInfo{{DeviceID: ml.DeviceID{ID: "gpu0"}, FreeMemory: uint64(128*format.MebiByte + minMemory)}, {DeviceID: ml.DeviceID{ID: "gpu1"}, Integrated: true, FreeMemory: uint64(256*format.MebiByte + minMemory)}}, + layers: []int{50 * format.MebiByte, 50 * format.MebiByte, 50 * format.MebiByte}, + numGPU: -1, + expected: ml.GPULayersList{{DeviceID: ml.DeviceID{ID: "gpu1"}, Layers: []int{0}}, {DeviceID: ml.DeviceID{ID: "gpu0"}, Layers: []int{1, 2}}}, + }, + { + name: "iGPU + dGPU fits on one", + gpus: []ml.DeviceInfo{{DeviceID: ml.DeviceID{ID: "gpu0"}, FreeMemory: uint64(128*format.MebiByte + minMemory)}, {DeviceID: ml.DeviceID{ID: "gpu1"}, Integrated: true, FreeMemory: uint64(256*format.MebiByte + minMemory)}}, + layers: []int{50 * format.MebiByte, 50 * format.MebiByte}, + numGPU: -1, + expected: ml.GPULayersList{{DeviceID: ml.DeviceID{ID: "gpu0"}, Layers: []int{0, 1}}}, + }, + { + name: "iGPU + dGPU partial", + gpus: []ml.DeviceInfo{{DeviceID: ml.DeviceID{ID: "gpu0"}, FreeMemory: uint64(128*format.MebiByte + minMemory)}, {DeviceID: ml.DeviceID{ID: "gpu1"}, Integrated: true, FreeMemory: uint64(256*format.MebiByte + minMemory)}}, + layers: []int{100 * format.MebiByte, 100 * format.MebiByte, 100 * format.MebiByte, 100 * format.MebiByte}, + numGPU: -1, + expected: ml.GPULayersList{{DeviceID: ml.DeviceID{ID: "gpu1"}, Layers: []int{0, 1}}, {DeviceID: ml.DeviceID{ID: "gpu0"}, Layers: []int{2}}}, + }, + { + name: "iGPU + dGPU numGPU 1", + gpus: []ml.DeviceInfo{{DeviceID: ml.DeviceID{ID: "gpu0"}, FreeMemory: uint64(128*format.MebiByte + minMemory)}, {DeviceID: ml.DeviceID{ID: "gpu1"}, Integrated: true, FreeMemory: uint64(256*format.MebiByte + minMemory)}}, + layers: []int{100 * format.MebiByte, 100 * format.MebiByte, 100 * format.MebiByte, 100 * format.MebiByte}, + numGPU: 1, + expected: ml.GPULayersList{{DeviceID: ml.DeviceID{ID: "gpu0"}, Layers: []int{2}}}, + }, + { + name: "iGPU + dGPU numGPU 999", + gpus: []ml.DeviceInfo{{DeviceID: ml.DeviceID{ID: "gpu0"}, FreeMemory: uint64(128*format.MebiByte + minMemory)}, {DeviceID: ml.DeviceID{ID: "gpu1"}, Integrated: true, FreeMemory: uint64(256*format.MebiByte + minMemory)}}, + layers: []int{100 * format.MebiByte, 100 * format.MebiByte, 100 * format.MebiByte, 100 * format.MebiByte}, + numGPU: 999, + expected: ml.GPULayersList{{DeviceID: ml.DeviceID{ID: "gpu0"}, Layers: []int{0}}, {DeviceID: ml.DeviceID{ID: "gpu1"}, Layers: []int{1, 2, 3}}}, + }, } for _, tt := range tests { @@ -145,12 +182,6 @@ func TestLLMServerFitGPU(t *testing.T) { systemInfo.FreeMemory = 512 * format.MebiByte systemInfo.FreeSwap = 256 * format.MebiByte - gpus := make([]ml.DeviceInfo, len(tt.gpus)) - for i := range tt.gpus { - gpus[i].DeviceID = tt.gpus[i].id - gpus[i].FreeMemory = uint64(tt.gpus[i].free) - } - s := &ollamaServer{ llmServer: llmServer{ totalLayers: uint64(len(tt.layers)), @@ -165,19 +196,19 @@ func TestLLMServerFitGPU(t *testing.T) { s.mem = &ml.BackendMemory{CPU: ml.DeviceMemory{ Weights: make([]uint64, s.totalLayers), Cache: make([]uint64, s.totalLayers), - }, GPUs: make([]ml.DeviceMemory, len(gpus))} + }, GPUs: make([]ml.DeviceMemory, len(tt.gpus))} for i := range tt.layers { s.mem.CPU.Weights[i] = uint64(tt.layers[i]) } for i := range s.mem.GPUs { - s.mem.GPUs[i].DeviceID = gpus[i].DeviceID + s.mem.GPUs[i].DeviceID = tt.gpus[i].DeviceID s.mem.GPUs[i].Weights = make([]uint64, s.totalLayers) s.mem.GPUs[i].Cache = make([]uint64, s.totalLayers) } - gpuLayers, err := s.createLayout(systemInfo, gpus, s.mem, tt.requireFull, 0) + gpuLayers, err := s.createLayout(systemInfo, tt.gpus, s.mem, tt.requireFull, 0) if err != tt.expectedErr { t.Fatalf("fitGPU returned error: %v", err) } diff --git a/ml/device.go b/ml/device.go index 040764fe..f0654127 100644 --- a/ml/device.go +++ b/ml/device.go @@ -367,6 +367,28 @@ func (a ByFreeMemory) Less(i, j int) bool { return a[i].FreeMemory < a[j].FreeMemory } +// ByPerformance groups devices by similar speed +func ByPerformance(l []DeviceInfo) [][]DeviceInfo { + resp := [][]DeviceInfo{} + scores := []bool{} + for _, info := range l { + found := false + requested := info.Integrated + for i, score := range scores { + if score == requested { + resp[i] = append(resp[i], info) + found = true + break + } + } + if !found { + scores = append(scores, requested) + resp = append(resp, []DeviceInfo{info}) + } + } + return resp +} + func ByLibrary(l []DeviceInfo) [][]DeviceInfo { resp := [][]DeviceInfo{} libs := []string{} From 15968714bd50970264bf44d7bfd72f0076919ed2 Mon Sep 17 00:00:00 2001 From: Bruce MacDonald Date: Tue, 11 Nov 2025 15:07:21 -0800 Subject: [PATCH 18/32] docs/openapi: document that delete and copy responses are empty (#13055) Some route endpoints return an empty response with a 200 OK. These should be documented in the OpenAPI doc. Note that the previous deletion response was not correct. --- docs/openapi.yaml | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 28e4eed6..6448bd9c 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -1275,6 +1275,9 @@ paths: example: source: gemma3 destination: gemma3-backup + responses: + "200": + description: Model successfully copied /api/pull: post: summary: Pull a model @@ -1382,16 +1385,7 @@ paths: model: gemma3 responses: "200": - description: Deletion status updates. - content: - application/json: - schema: - $ref: "#/components/schemas/StatusResponse" - example: - status: "success" - application/x-ndjson: - schema: - $ref: "#/components/schemas/StatusEvent" + description: Model successfully deleted /api/version: get: summary: Get version From 2d5e066c8cb2f54f398243dcb922e615ac94a509 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Tue, 11 Nov 2025 15:39:35 -0800 Subject: [PATCH 19/32] docs: fix openapi.yaml warnings, rename api.md to api-reference.md (#12904) --- docs/{api.md => api-reference.md} | 0 docs/openapi.yaml | 29 +++++++++++++++++++++-------- 2 files changed, 21 insertions(+), 8 deletions(-) rename docs/{api.md => api-reference.md} (100%) diff --git a/docs/api.md b/docs/api-reference.md similarity index 100% rename from docs/api.md rename to docs/api-reference.md diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 6448bd9c..61f2a6fa 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -2,12 +2,15 @@ openapi: 3.1.0 info: title: Ollama API version: 0.1.0 + license: + name: MIT + url: https://opensource.org/licenses/MIT description: | OpenAPI specification for the Ollama HTTP API - servers: - url: http://localhost:11434 - description: Local Ollama instance + description: Ollama +security: [] components: securitySchemes: bearerAuth: @@ -93,8 +96,11 @@ components: type: boolean default: true think: - type: boolean - description: When true, returns separate thinking output in addition to content + oneOf: + - type: boolean + - type: string + enum: [high, medium, low] + description: When true, returns separate thinking output in addition to content. Can be a boolean (true/false) or a string ("high", "medium", "low") for supported models. raw: type: boolean description: When true, returns the raw response from the model without any prompt templating @@ -271,8 +277,11 @@ components: type: boolean default: true think: - type: boolean - description: When true, returns separate thinking output in addition to content + oneOf: + - type: boolean + - type: string + enum: [high, medium, low] + description: When true, returns separate thinking output in addition to content. Can be a boolean (true/false) or a string ("high", "medium", "low") for supported models. keep_alive: oneOf: - type: string @@ -310,7 +319,6 @@ components: type: array items: type: string - nullable: true description: Optional base64-encoded images in the response done: type: boolean @@ -367,7 +375,6 @@ components: type: array items: type: string - nullable: true description: Partial base64-encoded images, when present done: type: boolean @@ -543,6 +550,9 @@ components: license: type: string description: The license of the model + modified_at: + type: string + description: Last modified timestamp in ISO 8601 format details: type: object description: High-level model details @@ -622,6 +632,9 @@ components: size_vram: type: integer description: VRAM usage in bytes + context_length: + type: integer + description: Context length for the running model PsResponse: type: object properties: From cb1cb06478af1a973092b163dddee2a256c1d2f3 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Tue, 11 Nov 2025 15:53:06 -0800 Subject: [PATCH 20/32] docs: rename api-reference.md back to api.md since redirect stopped working (#13056) --- docs/{api-reference.md => api.md} | 0 docs/api/{index.mdx => introduction.mdx} | 0 docs/docs.json | 6 +++++- 3 files changed, 5 insertions(+), 1 deletion(-) rename docs/{api-reference.md => api.md} (100%) rename docs/api/{index.mdx => introduction.mdx} (100%) diff --git a/docs/api-reference.md b/docs/api.md similarity index 100% rename from docs/api-reference.md rename to docs/api.md diff --git a/docs/api/index.mdx b/docs/api/introduction.mdx similarity index 100% rename from docs/api/index.mdx rename to docs/api/introduction.mdx diff --git a/docs/docs.json b/docs/docs.json index 6cc6606b..71a6f17a 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -63,6 +63,10 @@ { "source": "/api/openai", "destination": "/api/openai-compatibility" + }, + { + "source": "/api", + "destination": "/api/introduction" } ], "navigation": { @@ -130,7 +134,7 @@ { "group": "API Reference", "pages": [ - "/api/index", + "/api/introduction", "/api/authentication", "/api/streaming", "/api/usage", From 3a9e8e9fd42f32711b8aeea355e3ed5e155d49b2 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Wed, 12 Nov 2025 08:31:40 -0800 Subject: [PATCH 21/32] vulkan: temporary cary of vulkan fixes (#12971) This should be reverted once we update ggml past b6897 --- ..._vk_buffer_write_2d-from-ggml_vk_buf.patch | 32 + ...er-Dot-Refactor-and-K-Quant-support-.patch | 2140 +++++++++++++++++ ...pk_moe-fusion-to-handle-gpt-s-late-s.patch | 657 +++++ ...0032-vulkan-Fuse-rope-set_rows-16769.patch | 1242 ++++++++++ ...gsort-with-a-large-number-of-rows-16.patch | 85 + ...shmem-overrun-in-mmq-id-shader-16873.patch | 77 + ...-when-FP16-mul_mat-accumulation-is-n.patch | 80 + ml/backend/ggml/ggml/src/ggml-impl.h | 16 + .../ggml/src/ggml-metal/ggml-metal-device.cpp | 2 +- .../ggml/ggml/src/ggml-vulkan/ggml-vulkan.cpp | 830 +++++-- .../ggml-vulkan/vulkan-shaders/argsort.comp | 16 +- .../vulkan-shaders/dequant_funcs.glsl | 10 +- .../vulkan-shaders/dequant_funcs_cm2.glsl | 6 +- .../vulkan-shaders/dequant_mxfp4.comp | 4 +- .../vulkan-shaders/dequant_q2_k.comp | 4 +- .../vulkan-shaders/dequant_q4_k.comp | 4 +- .../vulkan-shaders/dequant_q5_k.comp | 4 +- .../vulkan-shaders/mul_mat_vec_q2_k.comp | 6 +- .../vulkan-shaders/mul_mat_vec_q4_k.comp | 6 +- .../vulkan-shaders/mul_mat_vec_q5_k.comp | 6 +- .../ggml-vulkan/vulkan-shaders/mul_mm.comp | 72 +- .../vulkan-shaders/mul_mm_funcs.glsl | 14 +- .../vulkan-shaders/mul_mm_id_funcs.glsl | 70 + .../ggml-vulkan/vulkan-shaders/mul_mmq.comp | 310 +-- .../vulkan-shaders/mul_mmq_funcs.glsl | 548 ++++- .../vulkan-shaders/mul_mmq_shmem_types.glsl | 78 + .../ggml-vulkan/vulkan-shaders/rope_head.glsl | 2 + .../ggml-vulkan/vulkan-shaders/rope_neox.comp | 13 +- .../ggml-vulkan/vulkan-shaders/rope_norm.comp | 13 +- .../ggml-vulkan/vulkan-shaders/topk_moe.comp | 96 +- .../src/ggml-vulkan/vulkan-shaders/types.glsl | 53 +- .../vulkan-shaders/vulkan-shaders-gen.cpp | 9 +- 32 files changed, 5838 insertions(+), 667 deletions(-) create mode 100644 llama/patches/0029-vulkan-Call-ggml_vk_buffer_write_2d-from-ggml_vk_buf.patch create mode 100644 llama/patches/0030-Vulkan-MMQ-Integer-Dot-Refactor-and-K-Quant-support-.patch create mode 100644 llama/patches/0031-vulkan-Update-topk_moe-fusion-to-handle-gpt-s-late-s.patch create mode 100644 llama/patches/0032-vulkan-Fuse-rope-set_rows-16769.patch create mode 100644 llama/patches/0033-vulkan-Handle-argsort-with-a-large-number-of-rows-16.patch create mode 100644 llama/patches/0034-vulkan-fix-shmem-overrun-in-mmq-id-shader-16873.patch create mode 100644 llama/patches/0035-vulkan-Fix-crash-when-FP16-mul_mat-accumulation-is-n.patch create mode 100644 ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm_id_funcs.glsl create mode 100644 ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq_shmem_types.glsl diff --git a/llama/patches/0029-vulkan-Call-ggml_vk_buffer_write_2d-from-ggml_vk_buf.patch b/llama/patches/0029-vulkan-Call-ggml_vk_buffer_write_2d-from-ggml_vk_buf.patch new file mode 100644 index 00000000..e9737aa4 --- /dev/null +++ b/llama/patches/0029-vulkan-Call-ggml_vk_buffer_write_2d-from-ggml_vk_buf.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jeff Bolz +Date: Wed, 29 Oct 2025 03:53:04 -0500 +Subject: [PATCH] vulkan: Call ggml_vk_buffer_write_2d from ggml_vk_buffer_copy + (#16793) + +This lets the copy to the destination device use the host-visible +vidmem optimization. +--- + ggml/src/ggml-vulkan/ggml-vulkan.cpp | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/ggml/src/ggml-vulkan/ggml-vulkan.cpp b/ggml/src/ggml-vulkan/ggml-vulkan.cpp +index 221e29509..18b7cbccf 100644 +--- a/ggml/src/ggml-vulkan/ggml-vulkan.cpp ++++ b/ggml/src/ggml-vulkan/ggml-vulkan.cpp +@@ -5654,14 +5654,11 @@ static void ggml_vk_buffer_copy(vk_buffer& dst, size_t dst_offset, vk_buffer& sr + VK_LOG_DEBUG("ggml_vk_buffer_copy(MULTI_DEVICE, " << size << ")"); + // Copy device to device + ggml_vk_ensure_sync_staging_buffer(src->device, size); +- ggml_vk_ensure_sync_staging_buffer(dst->device, size); + + // Copy to src staging buffer + ggml_vk_buffer_copy(src->device->sync_staging, 0, src, src_offset, size); +- // memcpy to dst staging buffer +- memcpy(dst->device->sync_staging->ptr, src->device->sync_staging->ptr, size); + // Copy to dst buffer +- ggml_vk_buffer_copy(dst, dst_offset, dst->device->sync_staging, 0, size); ++ ggml_vk_buffer_write_2d(dst, dst_offset, src->device->sync_staging->ptr, 0, size, 1); + } + } + diff --git a/llama/patches/0030-Vulkan-MMQ-Integer-Dot-Refactor-and-K-Quant-support-.patch b/llama/patches/0030-Vulkan-MMQ-Integer-Dot-Refactor-and-K-Quant-support-.patch new file mode 100644 index 00000000..1b1f65e4 --- /dev/null +++ b/llama/patches/0030-Vulkan-MMQ-Integer-Dot-Refactor-and-K-Quant-support-.patch @@ -0,0 +1,2140 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ruben Ortlam +Date: Wed, 29 Oct 2025 14:39:03 +0100 +Subject: [PATCH] Vulkan MMQ Integer Dot Refactor and K-Quant support (#16536) + +* vulkan: add mmq q2_k integer dot support + +* Refactor mmq caching + +* Reduce mmq register use + +* Load 4 quant blocks into shared memory in one step + +* Pack q2_k blocks into caches of 32 + +* Use 32-bit accumulators for integer dot matmul + +* Add q4_k mmq + +* Add q3_k mmq + +* Add q5_k mmq + +* Add q6_k mmq + +* Add mxfp4 mmq, enable MMQ MUL_MAT_ID + +* Fix mmv dm loads +--- + ggml/src/ggml-vulkan/ggml-vulkan.cpp | 165 +++++- + .../vulkan-shaders/dequant_funcs.glsl | 10 +- + .../vulkan-shaders/dequant_funcs_cm2.glsl | 6 +- + .../vulkan-shaders/dequant_mxfp4.comp | 4 +- + .../vulkan-shaders/dequant_q2_k.comp | 4 +- + .../vulkan-shaders/dequant_q4_k.comp | 4 +- + .../vulkan-shaders/dequant_q5_k.comp | 4 +- + .../vulkan-shaders/mul_mat_vec_q2_k.comp | 6 +- + .../vulkan-shaders/mul_mat_vec_q4_k.comp | 6 +- + .../vulkan-shaders/mul_mat_vec_q5_k.comp | 6 +- + .../ggml-vulkan/vulkan-shaders/mul_mm.comp | 72 +-- + .../vulkan-shaders/mul_mm_funcs.glsl | 14 +- + .../vulkan-shaders/mul_mm_id_funcs.glsl | 70 +++ + .../ggml-vulkan/vulkan-shaders/mul_mmq.comp | 288 +++------- + .../vulkan-shaders/mul_mmq_funcs.glsl | 538 ++++++++++++++++-- + .../vulkan-shaders/mul_mmq_shmem_types.glsl | 78 +++ + .../src/ggml-vulkan/vulkan-shaders/types.glsl | 53 +- + .../vulkan-shaders/vulkan-shaders-gen.cpp | 5 +- + 18 files changed, 928 insertions(+), 405 deletions(-) + create mode 100644 ggml/src/ggml-vulkan/vulkan-shaders/mul_mm_id_funcs.glsl + create mode 100644 ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq_shmem_types.glsl + +diff --git a/ggml/src/ggml-vulkan/ggml-vulkan.cpp b/ggml/src/ggml-vulkan/ggml-vulkan.cpp +index 18b7cbccf..53b57c179 100644 +--- a/ggml/src/ggml-vulkan/ggml-vulkan.cpp ++++ b/ggml/src/ggml-vulkan/ggml-vulkan.cpp +@@ -488,6 +488,7 @@ struct vk_device_struct { + vk_matmul_pipeline2 pipeline_matmul_id_f16_f32; + + vk_matmul_pipeline2 pipeline_dequant_mul_mat_mat_id[GGML_TYPE_COUNT]; ++ vk_matmul_pipeline2 pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_COUNT]; + + vk_pipeline pipeline_matmul_split_k_reduce; + vk_pipeline pipeline_quantize_q8_1; +@@ -2449,8 +2450,11 @@ static void ggml_vk_load_shaders(vk_device& device) { + l_warptile_id, m_warptile_id, s_warptile_id, + l_warptile_mmq, m_warptile_mmq, s_warptile_mmq, + l_warptile_mmq_int, m_warptile_mmq_int, s_warptile_mmq_int, ++ l_warptile_mmq_int_k, m_warptile_mmq_int_k, s_warptile_mmq_int_k, + l_warptile_mmq_k, m_warptile_mmq_k, s_warptile_mmq_k, +- l_warptile_mmqid, m_warptile_mmqid, s_warptile_mmqid; ++ l_warptile_mmqid, m_warptile_mmqid, s_warptile_mmqid, ++ l_warptile_mmqid_int, m_warptile_mmqid_int, s_warptile_mmqid_int, ++ l_warptile_mmqid_int_k, m_warptile_mmqid_int_k, s_warptile_mmqid_int_k; + std::array l_wg_denoms, m_wg_denoms, s_wg_denoms, + l_mmq_wg_denoms, m_mmq_wg_denoms, s_mmq_wg_denoms, + l_mmq_wg_denoms_k, m_mmq_wg_denoms_k, s_mmq_wg_denoms_k, +@@ -2513,10 +2517,16 @@ static void ggml_vk_load_shaders(vk_device& device) { + m_warptile_mmq = { 128, 64, 64, 32, subgroup_size_8, 32, 2, tm_m, tn_m, tk_m, subgroup_size_8 }; + s_warptile_mmq = { subgroup_size_32, 32, 32, 32, 32, 32, 2, tm_s, tn_s, tk_s, subgroup_size_8 }; + ++ // Integer MMQ has a smaller shared memory profile, but heavier register use + l_warptile_mmq_int = { 128, 128, 128, 32, subgroup_size_8 * 2, 64, 2, 4, 4, 1, subgroup_size_8 }; + m_warptile_mmq_int = { 128, 64, 64, 32, subgroup_size_8, 32, 2, 2, 2, 1, subgroup_size_8 }; + s_warptile_mmq_int = { subgroup_size_32, 32, 32, 32, 32, 32, 2, 2, 1, 1, subgroup_size_8 }; + ++ // K-quants use even more registers, mitigate by setting WMITER to 1 ++ l_warptile_mmq_int_k = { 128, 128, 128, 32, subgroup_size_8 * 2, 64, 1, 4, 4, 1, subgroup_size_8 }; ++ m_warptile_mmq_int_k = { 128, 64, 64, 32, subgroup_size_8, 32, 1, 2, 2, 1, subgroup_size_8 }; ++ s_warptile_mmq_int_k = { subgroup_size_32, 32, 32, 32, 32, 32, 1, 2, 1, 1, subgroup_size_8 }; ++ + l_warptile_id = { 128, 128, 128, 16, mul_mat_subgroup_size_16 * 2, 64, 2, tm_l, tn_l, tk_l, mul_mat_subgroup_size_16 }; + m_warptile_id = { 128, 64, 64, 16, mul_mat_subgroup_size_16, 32, 2, tm_m, tn_m, tk_m, mul_mat_subgroup_size_16 }; + s_warptile_id = { mul_mat_subgroup_size_16, 32, 32, 16, 32, 32, 2, tm_s, tn_s, tk_s, mul_mat_subgroup_size_16 }; +@@ -2525,10 +2535,18 @@ static void ggml_vk_load_shaders(vk_device& device) { + m_warptile_mmqid = { 128, 64, 64, 32, mul_mat_subgroup_size_8, 32, 2, tm_m, tn_m, tk_m, mul_mat_subgroup_size_8 }; + s_warptile_mmqid = { mul_mat_subgroup_size_32, 32, 32, 32, 32, 32, 2, tm_s, tn_s, tk_s, mul_mat_subgroup_size_8 }; + ++ l_warptile_mmqid_int = { 128, 128, 128, 32, mul_mat_subgroup_size_8 * 2, 64, 2, 4, 4, 1, mul_mat_subgroup_size_8 }; ++ m_warptile_mmqid_int = { 128, 64, 64, 32, mul_mat_subgroup_size_8, 32, 2, 2, 2, 1, mul_mat_subgroup_size_8 }; ++ s_warptile_mmqid_int = { mul_mat_subgroup_size_32, 32, 32, 32, 32, 32, 2, 2, 1, 1, mul_mat_subgroup_size_8 }; ++ ++ l_warptile_mmqid_int_k = { 128, 128, 128, 32, mul_mat_subgroup_size_16 * 2, 64, 1, 4, 4, 1, mul_mat_subgroup_size_16 }; ++ m_warptile_mmqid_int_k = { 128, 64, 64, 32, mul_mat_subgroup_size_16, 32, 1, 2, 2, 1, mul_mat_subgroup_size_16 }; ++ s_warptile_mmqid_int_k = { mul_mat_subgroup_size_32, 32, 32, 32, 32, 32, 1, 2, 1, 1, mul_mat_subgroup_size_16 }; ++ + // chip specific tuning + if ((device->architecture == AMD_GCN) && (device->driver_id != vk::DriverId::eAmdProprietary)) { + m_warptile_mmq = m_warptile_mmq_int = { 256, 64, 64, 32, 16, 16, 2, 2, 2, 1, 16 }; +- m_warptile_mmqid = { 256, 64, 64, 32, 16, 16, 2, 2, 2, 1, 16 }; ++ m_warptile_mmqid = m_warptile_mmqid_int = { 256, 64, 64, 32, 16, 16, 2, 2, 2, 1, 16 }; + } + + l_mmq_wg_denoms = l_wg_denoms = {128, 128, 1 }; +@@ -2913,18 +2931,15 @@ static void ggml_vk_load_shaders(vk_device& device) { + if (device->mul_mat ## ID ## _s[TYPE]) \ + ggml_vk_create_pipeline(device, device-> PIPELINE_NAME ->a_s, #NAMELC #F16ACC "_aligned_s", NAMELC ## _aligned ## F16ACC ## _len, NAMELC ## _aligned ## F16ACC ## _data, "main", PARAMCOUNT, sizeof(PUSHCONST), s_ ## WG_DENOMS, s_ ## WARPTILE, s_align, false, REQSUBGROUPSIZE > 0, REQSUBGROUPSIZE); \ + +-#define CREATE_MMQ(TYPE, PIPELINE_NAME, NAMELC, WG_DENOMS, WARPTILE, PUSHCONST, PARAMCOUNT, ID) \ ++#define CREATE_MMQ(TYPE, PIPELINE_NAME, NAMELC, WG_DENOMS, WARPTILE, PUSHCONST, PARAMCOUNT, ID, REQSUBGROUPSIZE) \ + if (device->mul_mat ## ID ## _l[TYPE]) { \ +- ggml_vk_create_pipeline(device, device-> PIPELINE_NAME .f16acc->l, #NAMELC "_f16acc_l", NAMELC ## _f16acc_len, NAMELC ## _f16acc_data, "main", PARAMCOUNT, sizeof(PUSHCONST), l_ ## WG_DENOMS, l_ ## WARPTILE, 1); \ +- ggml_vk_create_pipeline(device, device-> PIPELINE_NAME .f32acc->l, #NAMELC "_l", NAMELC ## _len, NAMELC ## _data, "main", PARAMCOUNT, sizeof(PUSHCONST), l_ ## WG_DENOMS, l_ ## WARPTILE, 1); \ ++ ggml_vk_create_pipeline(device, device-> PIPELINE_NAME .f32acc->l, #NAMELC "_l", NAMELC ## _len, NAMELC ## _data, "main", PARAMCOUNT, sizeof(PUSHCONST), l_ ## WG_DENOMS, l_ ## WARPTILE, 1, false, REQSUBGROUPSIZE > 0, REQSUBGROUPSIZE); \ + } \ + if (device->mul_mat ## ID ## _m[TYPE]) { \ +- ggml_vk_create_pipeline(device, device-> PIPELINE_NAME .f16acc->m, #NAMELC "_f16acc_m", NAMELC ## _f16acc_len, NAMELC ## _f16acc_data, "main", PARAMCOUNT, sizeof(PUSHCONST), m_ ## WG_DENOMS, m_ ## WARPTILE, 1); \ +- ggml_vk_create_pipeline(device, device-> PIPELINE_NAME .f32acc->m, #NAMELC "_m", NAMELC ## _len, NAMELC ## _data, "main", PARAMCOUNT, sizeof(PUSHCONST), m_ ## WG_DENOMS, m_ ## WARPTILE, 1); \ ++ ggml_vk_create_pipeline(device, device-> PIPELINE_NAME .f32acc->m, #NAMELC "_m", NAMELC ## _len, NAMELC ## _data, "main", PARAMCOUNT, sizeof(PUSHCONST), m_ ## WG_DENOMS, m_ ## WARPTILE, 1, false, REQSUBGROUPSIZE > 0, REQSUBGROUPSIZE); \ + } \ + if (device->mul_mat ## ID ## _s[TYPE]) { \ +- ggml_vk_create_pipeline(device, device-> PIPELINE_NAME .f16acc->s, #NAMELC "_f16acc_s", NAMELC ## _f16acc_len, NAMELC ## _f16acc_data, "main", PARAMCOUNT, sizeof(PUSHCONST), s_ ## WG_DENOMS, s_ ## WARPTILE, 1); \ +- ggml_vk_create_pipeline(device, device-> PIPELINE_NAME .f32acc->s, #NAMELC "_s", NAMELC ## _len, NAMELC ## _data, "main", PARAMCOUNT, sizeof(PUSHCONST), s_ ## WG_DENOMS, s_ ## WARPTILE, 1); \ ++ ggml_vk_create_pipeline(device, device-> PIPELINE_NAME .f32acc->s, #NAMELC "_s", NAMELC ## _len, NAMELC ## _data, "main", PARAMCOUNT, sizeof(PUSHCONST), s_ ## WG_DENOMS, s_ ## WARPTILE, 1, false, REQSUBGROUPSIZE > 0, REQSUBGROUPSIZE); \ + } \ + + // Create 2 variants, {f16,f32} accumulator +@@ -2963,11 +2978,19 @@ static void ggml_vk_load_shaders(vk_device& device) { + + #if defined(GGML_VULKAN_INTEGER_DOT_GLSLC_SUPPORT) + if (device->integer_dot_product) { +- CREATE_MMQ(GGML_TYPE_Q4_0, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q4_0], matmul_q4_0_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, ); +- CREATE_MMQ(GGML_TYPE_Q4_1, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q4_1], matmul_q4_1_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, ); +- CREATE_MMQ(GGML_TYPE_Q5_0, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q5_0], matmul_q5_0_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, ); +- CREATE_MMQ(GGML_TYPE_Q5_1, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q5_1], matmul_q5_1_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, ); +- CREATE_MMQ(GGML_TYPE_Q8_0, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q8_0], matmul_q8_0_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, ); ++ CREATE_MMQ(GGML_TYPE_Q4_0, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q4_0], matmul_q4_0_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, , 0); ++ CREATE_MMQ(GGML_TYPE_Q4_1, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q4_1], matmul_q4_1_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, , 0); ++ CREATE_MMQ(GGML_TYPE_Q5_0, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q5_0], matmul_q5_0_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, , 0); ++ CREATE_MMQ(GGML_TYPE_Q5_1, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q5_1], matmul_q5_1_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, , 0); ++ CREATE_MMQ(GGML_TYPE_Q8_0, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q8_0], matmul_q8_0_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, , 0); ++ ++ CREATE_MMQ(GGML_TYPE_MXFP4, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_MXFP4], matmul_mxfp4_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, , 0); ++ ++ CREATE_MMQ(GGML_TYPE_Q2_K, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q2_K], matmul_q2_k_q8_1, mmq_wg_denoms, warptile_mmq_int_k, vk_mat_mat_push_constants, 3, , 0); ++ CREATE_MMQ(GGML_TYPE_Q3_K, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q3_K], matmul_q3_k_q8_1, mmq_wg_denoms, warptile_mmq_int_k, vk_mat_mat_push_constants, 3, , 0); ++ CREATE_MMQ(GGML_TYPE_Q4_K, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q4_K], matmul_q4_k_q8_1, mmq_wg_denoms, warptile_mmq_int_k, vk_mat_mat_push_constants, 3, , 0); ++ CREATE_MMQ(GGML_TYPE_Q5_K, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q5_K], matmul_q5_k_q8_1, mmq_wg_denoms, warptile_mmq_int_k, vk_mat_mat_push_constants, 3, , 0); ++ CREATE_MMQ(GGML_TYPE_Q6_K, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q6_K], matmul_q6_k_q8_1, mmq_wg_denoms, warptile_mmq_int_k, vk_mat_mat_push_constants, 3, , 0); + } + #endif + +@@ -2997,6 +3020,24 @@ static void ggml_vk_load_shaders(vk_device& device) { + CREATE_MM2(GGML_TYPE_IQ4_XS, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_IQ4_XS], matmul_id_subgroup_iq4_xs_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size); + CREATE_MM2(GGML_TYPE_IQ4_NL, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_IQ4_NL], matmul_id_subgroup_iq4_nl_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size); + CREATE_MM2(GGML_TYPE_MXFP4, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_MXFP4], matmul_id_subgroup_mxfp4_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size); ++ ++#if defined(GGML_VULKAN_INTEGER_DOT_GLSLC_SUPPORT) ++ if (device->integer_dot_product) { ++ CREATE_MMQ(GGML_TYPE_Q4_0, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q4_0], matmul_id_subgroup_q4_0_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size); ++ CREATE_MMQ(GGML_TYPE_Q4_1, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q4_1], matmul_id_subgroup_q4_1_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size); ++ CREATE_MMQ(GGML_TYPE_Q5_0, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q5_0], matmul_id_subgroup_q5_0_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size); ++ CREATE_MMQ(GGML_TYPE_Q5_1, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q5_1], matmul_id_subgroup_q5_1_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size); ++ CREATE_MMQ(GGML_TYPE_Q8_0, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q8_0], matmul_id_subgroup_q8_0_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size); ++ ++ CREATE_MMQ(GGML_TYPE_MXFP4, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_MXFP4], matmul_id_subgroup_mxfp4_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size); ++ ++ CREATE_MMQ(GGML_TYPE_Q2_K, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q2_K], matmul_id_subgroup_q2_k_q8_1, mmq_wg_denoms, warptile_mmqid_int_k, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size_16); ++ CREATE_MMQ(GGML_TYPE_Q3_K, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q3_K], matmul_id_subgroup_q3_k_q8_1, mmq_wg_denoms, warptile_mmqid_int_k, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size_16); ++ CREATE_MMQ(GGML_TYPE_Q4_K, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q4_K], matmul_id_subgroup_q4_k_q8_1, mmq_wg_denoms, warptile_mmqid_int_k, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size_16); ++ CREATE_MMQ(GGML_TYPE_Q5_K, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q5_K], matmul_id_subgroup_q5_k_q8_1, mmq_wg_denoms, warptile_mmqid_int_k, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size_16); ++ CREATE_MMQ(GGML_TYPE_Q6_K, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q6_K], matmul_id_subgroup_q6_k_q8_1, mmq_wg_denoms, warptile_mmqid_int_k, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size_16); ++ } ++#endif + } else { + CREATE_MM(GGML_TYPE_F32, pipeline_matmul_id_f32, matmul_id_f32_f32, , wg_denoms, warptile, vk_mat_mat_push_constants, 4, _id, 0); + CREATE_MM2(GGML_TYPE_F16, pipeline_matmul_id_f16, matmul_id_f16, wg_denoms, warptile, vk_mat_mat_push_constants, 4, _id, 0); +@@ -3023,6 +3064,24 @@ static void ggml_vk_load_shaders(vk_device& device) { + CREATE_MM2(GGML_TYPE_IQ4_XS, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_IQ4_XS], matmul_id_iq4_xs_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, 4, _id, 0); + CREATE_MM2(GGML_TYPE_IQ4_NL, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_IQ4_NL], matmul_id_iq4_nl_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, 4, _id, 0); + CREATE_MM2(GGML_TYPE_MXFP4, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_MXFP4], matmul_id_mxfp4_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, 4, _id, 0); ++ ++#if defined(GGML_VULKAN_INTEGER_DOT_GLSLC_SUPPORT) ++ if (device->integer_dot_product) { ++ CREATE_MMQ(GGML_TYPE_Q4_0, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q4_0], matmul_id_q4_0_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, 0); ++ CREATE_MMQ(GGML_TYPE_Q4_1, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q4_1], matmul_id_q4_1_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, 0); ++ CREATE_MMQ(GGML_TYPE_Q5_0, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q5_0], matmul_id_q5_0_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, 0); ++ CREATE_MMQ(GGML_TYPE_Q5_1, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q5_1], matmul_id_q5_1_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, 0); ++ CREATE_MMQ(GGML_TYPE_Q8_0, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q8_0], matmul_id_q8_0_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, 0); ++ ++ CREATE_MMQ(GGML_TYPE_MXFP4, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_MXFP4], matmul_id_mxfp4_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, 0); ++ ++ CREATE_MMQ(GGML_TYPE_Q2_K, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q2_K], matmul_id_q2_k_q8_1, mmq_wg_denoms, warptile_mmqid_int_k, vk_mat_mat_id_push_constants, 4, _id, 0); ++ CREATE_MMQ(GGML_TYPE_Q3_K, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q3_K], matmul_id_q3_k_q8_1, mmq_wg_denoms, warptile_mmqid_int_k, vk_mat_mat_id_push_constants, 4, _id, 0); ++ CREATE_MMQ(GGML_TYPE_Q4_K, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q4_K], matmul_id_q4_k_q8_1, mmq_wg_denoms, warptile_mmqid_int_k, vk_mat_mat_id_push_constants, 4, _id, 0); ++ CREATE_MMQ(GGML_TYPE_Q5_K, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q5_K], matmul_id_q5_k_q8_1, mmq_wg_denoms, warptile_mmqid_int_k, vk_mat_mat_id_push_constants, 4, _id, 0); ++ CREATE_MMQ(GGML_TYPE_Q6_K, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q6_K], matmul_id_q6_k_q8_1, mmq_wg_denoms, warptile_mmqid_int_k, vk_mat_mat_id_push_constants, 4, _id, 0); ++ } ++#endif + } + #undef CREATE_MM2 + #undef CREATE_MMQ +@@ -3087,6 +3146,12 @@ static void ggml_vk_load_shaders(vk_device& device) { + CREATE_MMQ(GGML_TYPE_Q5_0, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q5_0].f32acc, matmul_q5_0_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, ); + CREATE_MMQ(GGML_TYPE_Q5_1, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q5_1].f32acc, matmul_q5_1_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, ); + CREATE_MMQ(GGML_TYPE_Q8_0, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q8_0].f32acc, matmul_q8_0_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, ); ++ ++ CREATE_MMQ(GGML_TYPE_Q2_K, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q2_K].f32acc, matmul_q2_k_q8_1, mmq_wg_denoms, warptile_mmq_int_k, vk_mat_mat_push_constants, 3, ); ++ CREATE_MMQ(GGML_TYPE_Q3_K, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q3_K].f32acc, matmul_q3_k_q8_1, mmq_wg_denoms, warptile_mmq_int_k, vk_mat_mat_push_constants, 3, ); ++ CREATE_MMQ(GGML_TYPE_Q4_K, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q4_K].f32acc, matmul_q4_k_q8_1, mmq_wg_denoms, warptile_mmq_int_k, vk_mat_mat_push_constants, 3, ); ++ CREATE_MMQ(GGML_TYPE_Q5_K, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q5_K].f32acc, matmul_q5_k_q8_1, mmq_wg_denoms, warptile_mmq_int_k, vk_mat_mat_push_constants, 3, ); ++ CREATE_MMQ(GGML_TYPE_Q6_K, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q6_K].f32acc, matmul_q6_k_q8_1, mmq_wg_denoms, warptile_mmq_int_k, vk_mat_mat_push_constants, 3, ); + } + #endif + +@@ -3146,7 +3211,7 @@ static void ggml_vk_load_shaders(vk_device& device) { + } + // reusing CREATE_MM from the fp32 path + if ((device->coopmat2 || device->coopmat_support) +-#if defined(GGML_VULKAN_INTEGER_DOT_GLSLC_SUPPORT) ++#if defined(GGML_VULKAN_BFLOAT16_GLSLC_SUPPORT) + && !device->coopmat_bf16_support + #endif + ) { +@@ -4930,7 +4995,7 @@ static vk_matmul_pipeline ggml_vk_get_mul_mat_mat_pipeline(ggml_backend_vk_conte + + // MMQ + if (src1_type == GGML_TYPE_Q8_1) { +- vk_matmul_pipeline pipelines = (ctx->device->fp16 && prec == GGML_PREC_DEFAULT) ? ctx->device->pipeline_dequant_mul_mat_mat_q8_1[src0_type].f16acc : ctx->device->pipeline_dequant_mul_mat_mat_q8_1[src0_type].f32acc; ++ vk_matmul_pipeline pipelines = ctx->device->pipeline_dequant_mul_mat_mat_q8_1[src0_type].f32acc; + + if (pipelines->s == nullptr && pipelines->m == nullptr && pipelines->l == nullptr) { + return nullptr; +@@ -5077,6 +5142,17 @@ static vk_matmul_pipeline ggml_vk_get_mul_mat_mat_id_pipeline(ggml_backend_vk_co + } + } + ++ // MMQ ++ if (src1_type == GGML_TYPE_Q8_1) { ++ vk_matmul_pipeline pipelines = ctx->device->pipeline_dequant_mul_mat_mat_id_q8_1[src0_type].f32acc; ++ ++ if (pipelines->s == nullptr && pipelines->m == nullptr && pipelines->l == nullptr) { ++ return nullptr; ++ } ++ ++ return pipelines; ++ } ++ + GGML_ASSERT(src1_type == GGML_TYPE_F32 || (ctx->device->coopmat2 && src1_type == GGML_TYPE_F16)); + + switch (src0_type) { +@@ -6879,10 +6955,19 @@ static void ggml_vk_mul_mat_id_q_f16(ggml_backend_vk_context * ctx, vk_context& + + const bool y_f32_kernel = src1->type == GGML_TYPE_F32 && !y_non_contig; + +- vk_matmul_pipeline mmp = ggml_vk_get_mul_mat_mat_id_pipeline(ctx, src0->type, y_non_contig ? f16_type : src1->type, (ggml_prec)dst->op_params[0]); ++ bool quantize_y = ctx->device->integer_dot_product && src1->type == GGML_TYPE_F32 && ggml_is_contiguous(src1) && (ne11 * ne10) % 4 == 0; ++ ++ // Check for mmq first ++ vk_matmul_pipeline mmp = quantize_y ? ggml_vk_get_mul_mat_mat_id_pipeline(ctx, src0->type, GGML_TYPE_Q8_1, (ggml_prec)dst->op_params[0]) : nullptr; ++ ++ if (mmp == nullptr) { ++ // Fall back to f16 dequant mul mat ++ mmp = ggml_vk_get_mul_mat_mat_id_pipeline(ctx, src0->type, y_non_contig ? f16_type : src1->type, (ggml_prec)dst->op_params[0]); ++ quantize_y = false; ++ } + + const bool qx_needs_dequant = mmp == nullptr || x_non_contig; +- const bool qy_needs_dequant = (src1->type != f16_type && !y_f32_kernel) || y_non_contig; ++ const bool qy_needs_dequant = !quantize_y && ((src1->type != f16_type && !y_f32_kernel) || y_non_contig); + + if (qx_needs_dequant) { + // Fall back to dequant + f16 mulmat +@@ -6892,8 +6977,8 @@ static void ggml_vk_mul_mat_id_q_f16(ggml_backend_vk_context * ctx, vk_context& + // Not implemented + GGML_ASSERT(y_non_contig || !qy_needs_dequant); // NOLINT + +- const uint32_t kpad = ggml_vk_align_size(ne10, ggml_vk_guess_matmul_id_pipeline_align(ctx, mmp, ne01, nei1, qx_needs_dequant ? f16_type : src0->type)); +- const bool aligned = ne10 == kpad && ne01 > 8 && nei1 > 8; ++ const uint32_t kpad = quantize_y ? 0 : ggml_vk_align_size(ne10, ggml_vk_guess_matmul_id_pipeline_align(ctx, mmp, ne01, nei1, qx_needs_dequant ? f16_type : src0->type)); ++ const bool aligned = !quantize_y && ne10 == kpad && ne01 > 8 && nei1 > 8; + + vk_pipeline pipeline = ggml_vk_guess_matmul_id_pipeline(ctx, mmp, ne01, nei1, aligned, qx_needs_dequant ? f16_type : src0->type); + +@@ -6906,12 +6991,13 @@ static void ggml_vk_mul_mat_id_q_f16(ggml_backend_vk_context * ctx, vk_context& + const uint64_t qx_sz = ggml_type_size(src0->type) * x_ne / ggml_blck_size(src0->type); + const uint64_t qy_sz = ggml_type_size(src1->type) * y_ne / ggml_blck_size(src1->type); + const uint64_t x_sz = !qx_needs_dequant ? qx_sz : sizeof(ggml_fp16_t) * x_ne; +- const uint64_t y_sz = y_f32_kernel ? sizeof(float) * y_ne : sizeof(ggml_fp16_t) * y_ne; ++ const uint64_t y_sz = quantize_y ? (y_ne * ggml_type_size(GGML_TYPE_Q8_1) / ggml_blck_size(GGML_TYPE_Q8_1)) : (y_f32_kernel ? sizeof(float) * y_ne : sizeof(ggml_fp16_t) * y_ne); + const uint64_t ids_sz = nbi2; + const uint64_t d_sz = sizeof(float) * d_ne; + + vk_pipeline to_fp16_vk_0 = nullptr; + vk_pipeline to_fp16_vk_1 = nullptr; ++ vk_pipeline to_q8_1 = nullptr; + + if (x_non_contig) { + to_fp16_vk_0 = ggml_vk_get_cpy_pipeline(ctx, src0, nullptr, f16_type); +@@ -6926,9 +7012,16 @@ static void ggml_vk_mul_mat_id_q_f16(ggml_backend_vk_context * ctx, vk_context& + GGML_ASSERT(!qx_needs_dequant || to_fp16_vk_0 != nullptr); // NOLINT + GGML_ASSERT(!qy_needs_dequant || to_fp16_vk_1 != nullptr); // NOLINT + ++ if (quantize_y) { ++ to_q8_1 = ggml_vk_get_quantize_pipeline(ctx, GGML_TYPE_Q8_1, true); ++ } ++ + if (dryrun) { + const uint64_t x_sz_upd = x_sz * ne02 * ne03; +- const uint64_t y_sz_upd = y_sz * ne12 * ne13; ++ uint64_t y_sz_upd = y_sz * ne12 * ne13; ++ if (quantize_y) { ++ y_sz_upd = CEIL_DIV(y_sz_upd, 144) * 144; ++ } + if ( + (qx_needs_dequant && x_sz_upd > ctx->device->properties.limits.maxStorageBufferRange) || + (qy_needs_dequant && y_sz_upd > ctx->device->properties.limits.maxStorageBufferRange)) { +@@ -6937,7 +7030,7 @@ static void ggml_vk_mul_mat_id_q_f16(ggml_backend_vk_context * ctx, vk_context& + if (qx_needs_dequant && ctx->prealloc_size_x < x_sz_upd) { + ctx->prealloc_size_x = x_sz_upd; + } +- if (qy_needs_dequant && ctx->prealloc_size_y < y_sz_upd) { ++ if ((qy_needs_dequant || quantize_y) && ctx->prealloc_size_y < y_sz_upd) { + ctx->prealloc_size_y = y_sz_upd; + } + +@@ -6949,6 +7042,9 @@ static void ggml_vk_mul_mat_id_q_f16(ggml_backend_vk_context * ctx, vk_context& + if (qy_needs_dequant) { + ggml_pipeline_request_descriptor_sets(ctx, to_fp16_vk_1, 1); + } ++ if (quantize_y) { ++ ggml_pipeline_request_descriptor_sets(ctx, to_q8_1, 1); ++ } + return; + } + +@@ -6985,6 +7081,9 @@ static void ggml_vk_mul_mat_id_q_f16(ggml_backend_vk_context * ctx, vk_context& + if (qy_needs_dequant) { + d_Y = ctx->prealloc_y; + GGML_ASSERT(d_Y->size >= y_sz * ne12 * ne13); ++ } else if (quantize_y) { ++ d_Y = ctx->prealloc_y; ++ GGML_ASSERT(d_Y->size >= CEIL_DIV(y_sz * ne12 * ne13, 144) * 144); + } else { + d_Y = d_Qy; + y_buf_offset = qy_buf_offset; +@@ -7016,6 +7115,17 @@ static void ggml_vk_mul_mat_id_q_f16(ggml_backend_vk_context * ctx, vk_context& + ctx->prealloc_y_last_tensor_used = src1; + } + } ++ if (quantize_y) { ++ if (ctx->prealloc_y_last_pipeline_used != to_q8_1.get() || ++ ctx->prealloc_y_last_tensor_used != src1) { ++ if (ctx->prealloc_y_need_sync) { ++ ggml_vk_sync_buffers(ctx, subctx); ++ } ++ ggml_vk_quantize_q8_1(ctx, subctx, ggml_vk_subbuffer(ctx, d_Qy, qy_buf_offset), ggml_vk_subbuffer(ctx, d_Y, 0), y_ne * ne12 * ne13, true); ++ ctx->prealloc_y_last_pipeline_used = to_q8_1.get(); ++ ctx->prealloc_y_last_tensor_used = src1; ++ } ++ } + + uint32_t stride_batch_x = ne00*ne01; + uint32_t stride_batch_y = ne10*ne11; +@@ -7024,14 +7134,19 @@ static void ggml_vk_mul_mat_id_q_f16(ggml_backend_vk_context * ctx, vk_context& + stride_batch_x = src0->nb[0] / ggml_type_size(src0->type); + } + +- if (!ggml_vk_dim01_contiguous(src1) && !qy_needs_dequant) { ++ if (!ggml_vk_dim01_contiguous(src1) && !qy_needs_dequant && !quantize_y) { + stride_batch_y = src1->nb[0] / ggml_type_size(src1->type); + } + ++ uint32_t y_sz_total = y_sz * ne12 * ne13; ++ if (quantize_y) { ++ y_sz_total = CEIL_DIV(y_sz_total, 144) * 144; ++ } ++ + // compute + ggml_vk_matmul_id( + ctx, subctx, pipeline, +- { d_X, x_buf_offset, x_sz * ne02 * ne03 }, { d_Y, y_buf_offset, y_sz * ne12 * ne13 }, ++ { d_X, x_buf_offset, x_sz * ne02 * ne03 }, { d_Y, y_buf_offset, y_sz_total }, + { d_D, d_buf_offset, d_sz * ne22 * ne23 }, { d_ids, ids_buf_offset, ids_sz }, + ne01, ne21, ne10, ne10, ne10, ne01, + stride_batch_x, stride_batch_y, ne20*ne21, +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/dequant_funcs.glsl b/ggml/src/ggml-vulkan/vulkan-shaders/dequant_funcs.glsl +index 0d98f5a9d..09676a623 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/dequant_funcs.glsl ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/dequant_funcs.glsl +@@ -437,7 +437,7 @@ vec4 dequantize4(uint ib, uint iqs, uint a_offset) { + #if defined(DATA_A_MXFP4) + vec2 dequantize(uint ib, uint iqs, uint a_offset) { + const uint vui = uint(data_a[a_offset + ib].qs[iqs]); +- return vec2(kvalues_mxfp4[vui & 0xF], kvalues_mxfp4[vui >> 4]); ++ return vec2(kvalues_mxfp4[vui & 0xF], kvalues_mxfp4[vui >> 4]) * 0.5; + } + vec4 dequantize4(uint ib, uint iqs, uint a_offset) { + vec2 v0 = dequantize(ib, iqs, a_offset); +@@ -488,9 +488,9 @@ vec2 dequantize(uint ib, uint iqs, uint a_offset) { + + const uvec2 qs = uvec2(data_a[a_offset + ib].qs[qsi], data_a[a_offset + ib].qs[qsi + 1]); + const uint scales = data_a[a_offset + ib].scales[scalesi]; +- const vec2 d = vec2(data_a[a_offset + ib].d); ++ const vec2 dm = vec2(data_a[a_offset + ib].dm); + +- return d.x * float(scales & 0xF) * vec2((qs >> qsshift) & 3) - d.y * float(scales >> 4); ++ return dm.x * float(scales & 0xF) * vec2((qs >> qsshift) & 3) - dm.y * float(scales >> 4); + } + vec2 get_dm(uint ib, uint a_offset) { + return vec2(1, 0); +@@ -529,7 +529,7 @@ vec2 dequantize(uint ib, uint iqs, uint a_offset) { + const uint is = 2 * n + b; // 0..7 + const uint qsi = n * 32 + (iqs % 16) * 2; // 0,2,4..126 + +- const vec2 loadd = vec2(data_a[a_offset + ib].d); ++ const vec2 loadd = vec2(data_a[a_offset + ib].dm); + + const uint scidx0 = (is < 4) ? is : (is + 4); + const uint scidx1 = (is < 4) ? is : (is - 4); +@@ -567,7 +567,7 @@ vec2 dequantize(uint ib, uint iqs, uint a_offset) { + + const uint8_t hm = uint8_t(1 << (iqs / 16)); + +- const vec2 loadd = vec2(data_a[a_offset + ib].d); ++ const vec2 loadd = vec2(data_a[a_offset + ib].dm); + + const uint scidx0 = (is < 4) ? is : (is + 4); + const uint scidx1 = (is < 4) ? is : (is - 4); +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/dequant_funcs_cm2.glsl b/ggml/src/ggml-vulkan/vulkan-shaders/dequant_funcs_cm2.glsl +index 67baedf7c..8ac6482dc 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/dequant_funcs_cm2.glsl ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/dequant_funcs_cm2.glsl +@@ -120,7 +120,7 @@ layout(buffer_reference, std430, buffer_reference_align = 16) buffer decodeBufQ2 + float16_t dequantFuncQ2_K(const in decodeBufQ2_K bl, const in uint blockCoords[2], const in uint coordInBlock[2]) + { + decodeBufQ2_K_packed16 bl16 = decodeBufQ2_K_packed16(bl); +- const f16vec2 d = bl.block.d; ++ const f16vec2 dm = bl.block.dm; + const uint idx = coordInBlock[1]; + + const uint scalesi = (idx & 0xF0) >> 4; // 0..15 +@@ -131,7 +131,7 @@ float16_t dequantFuncQ2_K(const in decodeBufQ2_K bl, const in uint blockCoords[2 + qs = unpack8(qs)[idx & 1]; + + const uint scales = bl.block.scales[scalesi]; +- float16_t ret = d.x * float16_t(scales & 0xF) * float16_t(qs) - d.y * float16_t(scales >> 4); ++ float16_t ret = dm.x * float16_t(scales & 0xF) * float16_t(qs) - dm.y * float16_t(scales >> 4); + return ret; + } + +@@ -680,7 +680,7 @@ float16_t dequantFuncMXFP4(const in decodeBufMXFP4 bl, const in uint blockCoords + uint32_t qs = bl.block.qs[iqs]; + qs >>= shift; + qs &= 0xF; +- float16_t ret = float16_t(kvalues_mxfp4[qs] * d); ++ float16_t ret = float16_t(kvalues_mxfp4[qs] * d * 0.5); + return ret; + } + #endif +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/dequant_mxfp4.comp b/ggml/src/ggml-vulkan/vulkan-shaders/dequant_mxfp4.comp +index ffba5a77d..3194ba291 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/dequant_mxfp4.comp ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/dequant_mxfp4.comp +@@ -26,7 +26,7 @@ void main() { + const float d = e8m0_to_fp32(data_a[ib].e); + + [[unroll]] for (uint l = 0; l < 8; ++l) { +- data_b[b_idx + l + 0] = D_TYPE(d * kvalues_mxfp4[data_a[ib].qs[q_idx + l] & 0xF]); +- data_b[b_idx + l + 16] = D_TYPE(d * kvalues_mxfp4[data_a[ib].qs[q_idx + l] >> 4]); ++ data_b[b_idx + l + 0] = D_TYPE(d * 0.5 * float(kvalues_mxfp4[data_a[ib].qs[q_idx + l] & 0xF])); ++ data_b[b_idx + l + 16] = D_TYPE(d * 0.5 * float(kvalues_mxfp4[data_a[ib].qs[q_idx + l] >> 4])); + } + } +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q2_k.comp b/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q2_k.comp +index 58dc2e5df..dc05a7834 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q2_k.comp ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q2_k.comp +@@ -24,8 +24,8 @@ void main() { + const uint ql_idx = 32 * ip + il; + const uint8_t qs = data_a[i].qs[32 * ip + il]; + +- FLOAT_TYPE dall = FLOAT_TYPE(data_a[i].d.x); +- FLOAT_TYPE dmin = FLOAT_TYPE(data_a[i].d.y); ++ FLOAT_TYPE dall = FLOAT_TYPE(data_a[i].dm.x); ++ FLOAT_TYPE dmin = FLOAT_TYPE(data_a[i].dm.y); + data_b[y_idx + 0] = D_TYPE(dall * FLOAT_TYPE((data_a[i].scales[is+0] & 0xF) * ((qs >> 0) & 3)) - dmin * FLOAT_TYPE(data_a[i].scales[is+0] >> 4)); + data_b[y_idx + 32] = D_TYPE(dall * FLOAT_TYPE((data_a[i].scales[is+2] & 0xF) * ((qs >> 2) & 3)) - dmin * FLOAT_TYPE(data_a[i].scales[is+2] >> 4)); + data_b[y_idx + 64] = D_TYPE(dall * FLOAT_TYPE((data_a[i].scales[is+4] & 0xF) * ((qs >> 4) & 3)) - dmin * FLOAT_TYPE(data_a[i].scales[is+4] >> 4)); +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q4_k.comp b/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q4_k.comp +index 8b7be557e..0f23dc0a3 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q4_k.comp ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q4_k.comp +@@ -20,8 +20,8 @@ void main() { + const uint is = 2 * il; + const uint n = 4; + +- const FLOAT_TYPE dall = FLOAT_TYPE(data_a[ib].d.x); +- const FLOAT_TYPE dmin = FLOAT_TYPE(data_a[ib].d.y); ++ const FLOAT_TYPE dall = FLOAT_TYPE(data_a[ib].dm.x); ++ const FLOAT_TYPE dmin = FLOAT_TYPE(data_a[ib].dm.y); + + const uint y_idx = ib * QUANT_K + 64 * il + n * ir; + const uint qs_idx = 32*il + n * ir; +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q5_k.comp b/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q5_k.comp +index 6bc04670f..970469a60 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q5_k.comp ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q5_k.comp +@@ -19,8 +19,8 @@ void main() { + const uint ir = tid % 16; + const uint is = 2 * il; + +- const FLOAT_TYPE dall = FLOAT_TYPE(data_a[ib].d.x); +- const FLOAT_TYPE dmin = FLOAT_TYPE(data_a[ib].d.y); ++ const FLOAT_TYPE dall = FLOAT_TYPE(data_a[ib].dm.x); ++ const FLOAT_TYPE dmin = FLOAT_TYPE(data_a[ib].dm.y); + + const uint y_idx = ib * QUANT_K + 64 * il + 2 * ir; + const uint qs_idx = 32*il + 2 * ir; +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q2_k.comp b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q2_k.comp +index 03ed25d3b..14093c0de 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q2_k.comp ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q2_k.comp +@@ -41,9 +41,7 @@ void calc_superblock(const uint a_offset, const uint b_offset, const uint itid, + const vec4 qs_u32_4 = vec4(unpack8((qs_u32 >> 4) & 0x03030303)); + const vec4 qs_u32_6 = vec4(unpack8((qs_u32 >> 6) & 0x03030303)); + +- vec2 d = vec2(data_a[ib0 + i].d); +- const FLOAT_TYPE dall = FLOAT_TYPE(d.x); +- const FLOAT_TYPE dmin = FLOAT_TYPE(d.y); ++ const FLOAT_TYPE_VEC2 dm = vec2(data_a[ib0 + i].dm); + + [[unroll]] for (uint j = 0; j < NUM_COLS; ++j) { + vec2 b0 = vec2(data_b_v2[(j*p.batch_stride_b + b_offset + y_idx) / 2 + 0]); +@@ -75,7 +73,7 @@ void calc_superblock(const uint a_offset, const uint b_offset, const uint itid, + fma(FLOAT_TYPE(b96[l]), sccache2[csel][ix][6 + 8*v_im], + fma(FLOAT_TYPE(b112[l]), sccache2[csel][ix][7 + 8*v_im], sum2)))))))); + } +- temp[j][n] = fma(dall, sum1, fma(-dmin, sum2, temp[j][n])); ++ temp[j][n] = fma(dm.x, sum1, fma(-dm.y, sum2, temp[j][n])); + } + } + } +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q4_k.comp b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q4_k.comp +index 21d07d2e5..49d91ad59 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q4_k.comp ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q4_k.comp +@@ -14,9 +14,7 @@ void calc_superblock(const uint a_offset, const uint b_offset, const uint v_im, + + [[unroll]] for (uint n = 0; n < num_rows; ++n) { + const uint ib0 = a_offset / QUANT_K + (first_row+n)*num_blocks_per_row; +- vec2 d = vec2(data_a[ib0 + i].d); +- const FLOAT_TYPE dall = FLOAT_TYPE(d.x); +- const FLOAT_TYPE dmin = FLOAT_TYPE(d.y); ++ const FLOAT_TYPE_VEC2 dm = FLOAT_TYPE_VEC2(data_a[ib0 + i].dm); + + const uint32_t scale0_u32 = data_a_packed16[ib0 + i].scales[v_im ]; + const uint32_t scale4_u32 = data_a_packed16[ib0 + i].scales[v_im + 2]; +@@ -81,7 +79,7 @@ void calc_superblock(const uint a_offset, const uint b_offset, const uint v_im, + fma(FLOAT_TYPE(by10.y), sc2, fma(FLOAT_TYPE(by132.y), sc3, fma(FLOAT_TYPE(by20.y), sc6, fma(FLOAT_TYPE(by232.y), sc7, + fma(FLOAT_TYPE(by10.z), sc2, fma(FLOAT_TYPE(by132.z), sc3, fma(FLOAT_TYPE(by20.z), sc6, fma(FLOAT_TYPE(by232.z), sc7, + fma(FLOAT_TYPE(by10.w), sc2, fma(FLOAT_TYPE(by132.w), sc3, fma(FLOAT_TYPE(by20.w), sc6, FLOAT_TYPE(by232.w) * sc7))))))))))))))); +- temp[j][n] = fma(dall, fma(sx, sc0, fma(sy, sc1, fma(sz, sc4, sw * sc5))), fma(-dmin, smin, temp[j][n])); ++ temp[j][n] = fma(dm.x, fma(sx, sc0, fma(sy, sc1, fma(sz, sc4, sw * sc5))), fma(-dm.y, smin, temp[j][n])); + } + } + } +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q5_k.comp b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q5_k.comp +index 9e46c89a1..0d61b4966 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q5_k.comp ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q5_k.comp +@@ -14,9 +14,7 @@ void calc_superblock(const uint a_offset, const uint b_offset, const uint v_im, + + [[unroll]] for (uint n = 0; n < num_rows; ++n) { + const uint ib0 = a_offset / QUANT_K + (first_row+n)*num_blocks_per_row; +- vec2 d = vec2(data_a[ib0 + i].d); +- const FLOAT_TYPE dall = FLOAT_TYPE(d.x); +- const FLOAT_TYPE dmin = FLOAT_TYPE(d.y); ++ const FLOAT_TYPE_VEC2 dm = FLOAT_TYPE_VEC2(data_a[ib0 + i].dm); + + const uint32_t scale0_u32 = data_a_packed16[ib0 + i].scales[v_im ]; + const uint32_t scale4_u32 = data_a_packed16[ib0 + i].scales[v_im + 2]; +@@ -113,7 +111,7 @@ void calc_superblock(const uint a_offset, const uint b_offset, const uint v_im, + fma(FLOAT_TYPE(by132.x) + FLOAT_TYPE(by132.y) + FLOAT_TYPE(by148.x) + FLOAT_TYPE(by148.y), sc3, + fma(FLOAT_TYPE(by20.x) + FLOAT_TYPE(by20.y) + FLOAT_TYPE(by216.x) + FLOAT_TYPE(by216.y), sc6, + (FLOAT_TYPE(by232.x) + FLOAT_TYPE(by232.y) + FLOAT_TYPE(by248.x) + FLOAT_TYPE(by248.y)) * sc7))); +- temp[j][n] = fma(dall, fma(sx, sc0, fma(sy, sc1, fma(sz, sc4, sw * sc5))), fma(-dmin, smin, temp[j][n])); ++ temp[j][n] = fma(dm.x, fma(sx, sc0, fma(sy, sc1, fma(sz, sc4, sw * sc5))), fma(-dm.y, smin, temp[j][n])); + } + } + } +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm.comp b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm.comp +index a20788c4b..d260969f0 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm.comp ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm.comp +@@ -120,81 +120,11 @@ shared FLOAT_TYPE_VEC2 buf_b[BN * SHMEM_STRIDE]; + + #define NUM_WARPS (BLOCK_SIZE / WARP) + +-#ifdef MUL_MAT_ID +-shared u16vec2 row_ids[BN]; +-uint _ne1; +- +-#ifdef MUL_MAT_ID_USE_SUBGROUPS +-shared uvec4 ballots_sh[NUM_WARPS]; +- +-void load_row_ids(uint expert_idx, bool nei0_is_pow2, uint ic) { +- _ne1 = 0; +- uint num_elements = p.nei1 * p.nei0; +- uint nei0shift = findLSB(p.nei0); +- +- uint ids[16]; +- uint iter = 0; +- +- for (uint j = 0; j < num_elements; j += BLOCK_SIZE) { +- // prefetch up to 16 elements +- if (iter == 0) { +- [[unroll]] for (uint k = 0; k < 16; ++k) { +- uint i = j + gl_LocalInvocationIndex + k*BLOCK_SIZE; +- bool in_range = i < num_elements; +- uint ii1; +- if (nei0_is_pow2) { +- ii1 = i >> nei0shift; +- } else { +- ii1 = i / p.nei0; +- } +- uint ii0 = i - ii1 * p.nei0; +- ids[k] = in_range ? data_ids[ii1*p.nbi1 + ii0] : 0; +- } +- } +- uint i = j + gl_LocalInvocationIndex; +- bool in_range = i < num_elements; +- uint ii1; +- if (nei0_is_pow2) { +- ii1 = i >> nei0shift; +- } else { +- ii1 = i / p.nei0; +- } +- uint ii0 = i - ii1 * p.nei0; +- uint id = ids[iter++]; +- uvec4 ballot = subgroupBallot(in_range && id == expert_idx); +- +- ballots_sh[gl_SubgroupID] = ballot; +- barrier(); +- +- uint subgroup_base = 0; +- uint total = 0; +- for (uint k = 0; k < gl_NumSubgroups; ++k) { +- if (k == gl_SubgroupID) { +- subgroup_base = total; +- } +- total += subgroupBallotBitCount(ballots_sh[k]); +- } +- barrier(); +- +- uint idx = subgroup_base + subgroupBallotExclusiveBitCount(ballot); +- if (in_range && id == expert_idx && _ne1 + idx >= ic * BN && _ne1 + idx < (ic + 1) * BN) { +- row_ids[_ne1 + idx - ic * BN] = u16vec2(ii0, ii1); +- } +- _ne1 += total; +- iter &= 15; +- if (_ne1 >= (ic + 1) * BN) { +- break; +- } +- } +- barrier(); +-} +-#endif // MUL_MAT_ID_USE_SUBGROUPS +-#endif // MUL_MAT_ID +- + #ifdef COOPMAT + shared ACC_TYPE coopmat_stage[TM * TN * NUM_WARPS]; + #endif + ++#include "mul_mm_id_funcs.glsl" + #include "mul_mm_funcs.glsl" + + void main() { +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm_funcs.glsl b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm_funcs.glsl +index 0ebfbd646..ee5ded2e8 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm_funcs.glsl ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm_funcs.glsl +@@ -134,15 +134,15 @@ void load_a_to_shmem(const uint pos_a, const uint row, const uint col, const uin + const uint ib = idx / 128; // 2 values per idx + const uint iqs = idx % 128; // 0..127 + +- const uint qsi = (iqs / 64) * 32 + (iqs % 16) * 2; // 0,2,4..30 ++ const uint qsi = (iqs / 64) * 16 + (iqs % 16); // 0..15 + const uint scalesi = iqs / 8; // 0..15 + const uint qsshift = ((iqs % 64) / 16) * 2; // 0,2,4,6 + +- const uvec2 qs = uvec2(data_a[ib].qs[qsi], data_a[ib].qs[qsi + 1]); ++ const uvec2 qs = uvec2(unpack8(data_a_packed16[ib].qs[qsi])); + const uint scales = data_a[ib].scales[scalesi]; +- const vec2 d = vec2(data_a[ib].d); ++ const vec2 dm = vec2(data_a[ib].dm); + +- const vec2 v = d.x * float(scales & 0xF) * vec2((qs >> qsshift) & 3) - d.y * float(scales >> 4); ++ const vec2 v = dm.x * float(scales & 0xF) * vec2((qs >> qsshift) & 3) - dm.y * float(scales >> 4); + + buf_a[buf_idx] = FLOAT_TYPE_VEC2(v.xy); + #elif defined(DATA_A_Q3_K) +@@ -179,7 +179,7 @@ void load_a_to_shmem(const uint pos_a, const uint row, const uint col, const uin + const uint is = 2 * n + b; // 0..7 + const uint qsi = n * 32 + (iqs % 16) * 2; // 0,2,4..126 + +- const vec2 loadd = vec2(data_a[ib].d); ++ const vec2 loadd = vec2(data_a[ib].dm); + + const uint scidx0 = (is < 4) ? is : (is + 4); + const uint scidx1 = (is < 4) ? is : (is - 4); +@@ -215,7 +215,7 @@ void load_a_to_shmem(const uint pos_a, const uint row, const uint col, const uin + + const uint8_t hm = uint8_t(1 << (iqs / 16)); + +- const vec2 loadd = vec2(data_a[ib].d); ++ const vec2 loadd = vec2(data_a[ib].dm); + + const uint scidx0 = (is < 4) ? is : (is + 4); + const uint scidx1 = (is < 4) ? is : (is - 4); +@@ -468,7 +468,7 @@ void load_a_to_shmem(const uint pos_a, const uint row, const uint col, const uin + const uint ib = idx / 8; + const uint iqs = (idx & 0x07) * 2; + +- const float d = e8m0_to_fp32(data_a[ib].e); ++ const float d = e8m0_to_fp32(data_a[ib].e) * 0.5; + const uint vui = uint(data_a[ib].qs[iqs]); + const uint vui2 = uint(data_a[ib].qs[iqs+1]); + +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm_id_funcs.glsl b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm_id_funcs.glsl +new file mode 100644 +index 000000000..1d0e84ac9 +--- /dev/null ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm_id_funcs.glsl +@@ -0,0 +1,70 @@ ++#ifdef MUL_MAT_ID ++shared u16vec2 row_ids[BN]; ++uint _ne1; ++ ++#ifdef MUL_MAT_ID_USE_SUBGROUPS ++shared uvec4 ballots_sh[NUM_WARPS]; ++ ++void load_row_ids(uint expert_idx, bool nei0_is_pow2, uint ic) { ++ _ne1 = 0; ++ uint num_elements = p.nei1 * p.nei0; ++ uint nei0shift = findLSB(p.nei0); ++ ++ uint ids[16]; ++ uint iter = 0; ++ ++ for (uint j = 0; j < num_elements; j += BLOCK_SIZE) { ++ // prefetch up to 16 elements ++ if (iter == 0) { ++ [[unroll]] for (uint k = 0; k < 16; ++k) { ++ uint i = j + gl_LocalInvocationIndex + k*BLOCK_SIZE; ++ bool in_range = i < num_elements; ++ uint ii1; ++ if (nei0_is_pow2) { ++ ii1 = i >> nei0shift; ++ } else { ++ ii1 = i / p.nei0; ++ } ++ uint ii0 = i - ii1 * p.nei0; ++ ids[k] = in_range ? data_ids[ii1*p.nbi1 + ii0] : 0; ++ } ++ } ++ uint i = j + gl_LocalInvocationIndex; ++ bool in_range = i < num_elements; ++ uint ii1; ++ if (nei0_is_pow2) { ++ ii1 = i >> nei0shift; ++ } else { ++ ii1 = i / p.nei0; ++ } ++ uint ii0 = i - ii1 * p.nei0; ++ uint id = ids[iter++]; ++ uvec4 ballot = subgroupBallot(in_range && id == expert_idx); ++ ++ ballots_sh[gl_SubgroupID] = ballot; ++ barrier(); ++ ++ uint subgroup_base = 0; ++ uint total = 0; ++ for (uint k = 0; k < gl_NumSubgroups; ++k) { ++ if (k == gl_SubgroupID) { ++ subgroup_base = total; ++ } ++ total += subgroupBallotBitCount(ballots_sh[k]); ++ } ++ barrier(); ++ ++ uint idx = subgroup_base + subgroupBallotExclusiveBitCount(ballot); ++ if (in_range && id == expert_idx && _ne1 + idx >= ic * BN && _ne1 + idx < (ic + 1) * BN) { ++ row_ids[_ne1 + idx - ic * BN] = u16vec2(ii0, ii1); ++ } ++ _ne1 += total; ++ iter &= 15; ++ if (_ne1 >= (ic + 1) * BN) { ++ break; ++ } ++ } ++ barrier(); ++} ++#endif // MUL_MAT_ID_USE_SUBGROUPS ++#endif // MUL_MAT_ID +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq.comp b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq.comp +index b5d761c0b..8b238ac4b 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq.comp ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq.comp +@@ -10,10 +10,9 @@ + #extension GL_EXT_shader_explicit_arithmetic_types_float16 : require + #endif + +-#ifdef COOPMAT +-#extension GL_KHR_cooperative_matrix : enable +-#extension GL_KHR_memory_scope_semantics : enable ++#if defined(MUL_MAT_ID_USE_SUBGROUPS) + #extension GL_KHR_shader_subgroup_basic : enable ++#extension GL_KHR_shader_subgroup_ballot : enable + #endif + + #ifdef MUL_MAT_ID +@@ -24,7 +23,10 @@ + + layout(local_size_x_id = 0, local_size_y = 1, local_size_z = 1) in; + +-layout (binding = 0) readonly buffer A {A_TYPE_PACKED16 data_a[];}; ++layout (binding = 0) readonly buffer A {A_TYPE data_a[];}; ++#if defined(A_TYPE_PACKED16) ++layout (binding = 0) readonly buffer A_PACKED16 {A_TYPE_PACKED16 data_a_packed16[];}; ++#endif + #if defined(A_TYPE_PACKED32) + layout (binding = 0) readonly buffer A_PACKED32 {A_TYPE_PACKED32 data_a_packed32[];}; + #endif +@@ -76,40 +78,27 @@ layout (constant_id = 10) const uint WARP = 32; + + #define BK 32 + +-#ifdef COOPMAT +-#define SHMEM_STRIDE (BK / 4 + 4) +-#else +-#define SHMEM_STRIDE (BK / 4 + 1) +-#endif ++#define MMQ_SHMEM + +-shared int32_t buf_a_qs[BM * SHMEM_STRIDE]; ++#include "mul_mmq_shmem_types.glsl" + +-#ifndef COOPMAT +-#if QUANT_AUXF == 1 +-shared FLOAT_TYPE buf_a_dm[BM]; +-#else +-shared FLOAT_TYPE_VEC2 buf_a_dm[BM]; +-#endif ++#ifndef BK_STEP ++#define BK_STEP 4 + #endif + +-shared int32_t buf_b_qs[BN * SHMEM_STRIDE]; +-#ifndef COOPMAT +-shared FLOAT_TYPE_VEC2 buf_b_ds[BN]; +-#endif ++// Shared memory cache ++shared block_a_cache buf_a[BM * BK_STEP]; ++shared block_b_cache buf_b[BN * BK_STEP]; ++// Register cache ++block_a_cache cache_a[WMITER * TM]; ++block_b_cache cache_b; + +-#define LOAD_VEC_A (4 * QUANT_R) ++#define LOAD_VEC_A (4 * QUANT_R_MMQ) + #define LOAD_VEC_B 16 + +-#ifdef MUL_MAT_ID +-shared u16vec2 row_ids[4096]; +-#endif // MUL_MAT_ID +- + #define NUM_WARPS (BLOCK_SIZE / WARP) + +-#ifdef COOPMAT +-shared ACC_TYPE coopmat_stage[TM * TN * NUM_WARPS]; +-#endif +- ++#include "mul_mm_id_funcs.glsl" + #include "mul_mmq_funcs.glsl" + + void main() { +@@ -139,26 +128,12 @@ void main() { + const uint WNITER = (WM * WN) / (WARP * TM * TN * WMITER); + const uint WSUBM = WM / WMITER; + const uint WSUBN = WN / WNITER; +- +-#ifdef COOPMAT +- const uint warp_i = gl_SubgroupID; +- +- const uint tiw = gl_SubgroupInvocationID; +- +- const uint cms_per_row = WM / TM; +- const uint cms_per_col = WN / TN; +- +- const uint storestride = WARP / TM; +- const uint store_r = tiw % TM; +- const uint store_c = tiw / TM; +-#else + const uint warp_i = gl_LocalInvocationID.x / WARP; + + const uint tiw = gl_LocalInvocationID.x % WARP; + + const uint tiwr = tiw % (WSUBM / TM); + const uint tiwc = tiw / (WSUBM / TM); +-#endif + + const uint warp_r = warp_i % (BM / WM); + const uint warp_c = warp_i / (BM / WM); +@@ -172,17 +147,27 @@ void main() { + const uint loadstride_b = BLOCK_SIZE * LOAD_VEC_B / BK; + + #ifdef MUL_MAT_ID +- uint _ne1 = 0; +- for (uint ii1 = 0; ii1 < p.nei1; ii1++) { +- for (uint ii0 = 0; ii0 < p.nei0; ii0++) { ++#ifdef MUL_MAT_ID_USE_SUBGROUPS ++ if (bitCount(p.nei0) == 1) { ++ load_row_ids(expert_idx, true, ic); ++ } else { ++ load_row_ids(expert_idx, false, ic); ++ } ++#else ++ _ne1 = 0; ++ for (uint ii1 = 0; ii1 < p.nei1 && _ne1 < (ic + 1) * BN; ii1++) { ++ for (uint ii0 = 0; ii0 < p.nei0 && _ne1 < (ic + 1) * BN; ii0++) { + if (data_ids[ii1*p.nbi1 + ii0] == expert_idx) { +- row_ids[_ne1] = u16vec2(ii0, ii1); ++ if (_ne1 >= ic * BN) { ++ row_ids[_ne1 - ic * BN] = u16vec2(ii0, ii1); ++ } + _ne1++; + } + } + } + + barrier(); ++#endif + + // Workgroup has no work + if (ic * BN >= _ne1) return; +@@ -209,159 +194,70 @@ void main() { + uint pos_b_ib = (batch_idx * p.batch_stride_b + ic * BN * p.stride_b + start_k) / BK; + #endif + +-#ifdef COOPMAT +- coopmat cache_a; +- coopmat cache_b; +- coopmat cm_result; +- +- coopmat factors[cms_per_row * cms_per_col]; +- +- coopmat sums[cms_per_row * cms_per_col]; +- +- [[unroll]] for (uint i = 0; i < cms_per_row * cms_per_col; i++) { +- sums[i] = coopmat(0.0f); +- } +-#else +- int32_t cache_a_qs[WMITER * TM * BK / 4]; +- +- int32_t cache_b_qs[TN * BK / 4]; +- + ACC_TYPE sums[WMITER * TM * WNITER * TN]; + + [[unroll]] for (uint i = 0; i < WMITER*TM*WNITER*TN; i++) { + sums[i] = ACC_TYPE(0.0f); + } +-#endif + +-#if QUANT_AUXF == 1 +- FLOAT_TYPE cache_a_dm[WMITER * TM]; +-#else +- FLOAT_TYPE_VEC2 cache_a_dm[WMITER * TM]; +-#endif +- +- FLOAT_TYPE_VEC2 cache_b_ds[TN]; +- +- for (uint block = start_k; block < end_k; block += BK) { ++ for (uint block = start_k; block < end_k; block += BK * BK_STEP) { + [[unroll]] for (uint l = 0; loadc_a + l < BM; l += loadstride_a) { +- const uint ib = pos_a_ib + (loadc_a + l) * p.stride_a / BK; +- const uint iqs = loadr_a; + const uint buf_ib = loadc_a + l; ++ const uint ib = pos_a_ib + buf_ib * p.stride_a / BK; ++ const uint iqs = loadr_a; + +- if (iqs == 0) { +-#if QUANT_AUXF == 1 +- buf_a_dm[buf_ib] = get_d(ib); +-#else +- buf_a_dm[buf_ib] = get_dm(ib); +-#endif ++ [[unroll]] for (uint k_step = 0; k_step < BK_STEP; k_step++) { ++ block_a_to_shmem(k_step * BM + buf_ib, ib + k_step, iqs); + } +-#if QUANT_R == 1 +- buf_a_qs[buf_ib * SHMEM_STRIDE + iqs] = repack(ib, iqs); +-#else +- const i32vec2 vals = repack(ib, iqs); +- buf_a_qs[buf_ib * SHMEM_STRIDE + iqs ] = vals.x; +- buf_a_qs[buf_ib * SHMEM_STRIDE + iqs + 4] = vals.y; +-#endif + } + [[unroll]] for (uint l = 0; loadc_b + l < BN; l += loadstride_b) { ++ const uint buf_ib = loadc_b + l; ++ + #ifdef MUL_MAT_ID +- const u16vec2 row_idx = row_ids[ic * BN + loadc_b + l]; +- const uint idx = pos_b_ib + row_idx.y * p.batch_stride_b / LOAD_VEC_B + (row_idx.x % p.ne11) * p.stride_b / LOAD_VEC_B + loadr_b; +- const uint ib = idx / 8; +- const uint iqs = idx & 0x7; ++ const u16vec2 row_idx = row_ids[buf_ib]; ++ const uint ib = pos_b_ib + row_idx.y * p.batch_stride_b / BK + (row_idx.x % p.ne11) * p.stride_b / BK; + #else +- const uint ib = pos_b_ib + (loadc_b + l) * p.stride_b / BK; +- const uint ib_outer = ib / 4; +- const uint ib_inner = ib % 4; +- +- const uint iqs = loadr_b; ++ const uint ib = pos_b_ib + buf_ib * p.stride_b / BK; + #endif ++ const uint iqs = loadr_b; + +- const uint buf_ib = loadc_b + l; +- +- if (iqs == 0) { +- buf_b_ds[buf_ib] = FLOAT_TYPE_VEC2(data_b[ib_outer].ds[ib_inner]); ++ [[unroll]] for (uint k_step = 0; k_step < BK_STEP; k_step++) { ++ block_b_to_shmem(k_step * BN + buf_ib, ib + k_step, iqs); + } +- const ivec4 values = data_b[ib_outer].qs[ib_inner * 2 + iqs]; +- buf_b_qs[buf_ib * SHMEM_STRIDE + iqs * 4 ] = values.x; +- buf_b_qs[buf_ib * SHMEM_STRIDE + iqs * 4 + 1] = values.y; +- buf_b_qs[buf_ib * SHMEM_STRIDE + iqs * 4 + 2] = values.z; +- buf_b_qs[buf_ib * SHMEM_STRIDE + iqs * 4 + 3] = values.w; + } + + barrier(); + +- pos_a_ib += 1; +- pos_b_ib += 1; ++ pos_a_ib += BK_STEP; ++ pos_b_ib += BK_STEP; + +-#ifdef COOPMAT +- [[unroll]] for (uint cm_row = 0; cm_row < cms_per_row; cm_row++) { +- const uint ib_a = warp_r * WM + cm_row * TM; ++ for (uint k_step = 0; k_step < BK_STEP; k_step++) { + // Load from shared into cache +- coopMatLoad(cache_a, buf_a_qs, ib_a * SHMEM_STRIDE, SHMEM_STRIDE, gl_CooperativeMatrixLayoutRowMajor); +- +- // TODO: only cache values that are actually needed +- [[unroll]] for (uint t_idx = 0; t_idx < TM; t_idx++) { +- cache_a_dm[t_idx] = buf_a_dm[ib_a + t_idx]; +- } +- +- [[unroll]] for (uint cm_col = 0; cm_col < cms_per_col; cm_col++) { +- const uint ib_b = warp_c * WN + cm_col * TN; +- coopMatLoad(cache_b, buf_b_qs, ib_b * SHMEM_STRIDE, SHMEM_STRIDE, gl_CooperativeMatrixLayoutColumnMajor); +- +- // TODO: only cache values that are actually needed +- [[unroll]] for (uint t_idx = 0; t_idx < TN; t_idx++) { +- cache_b_dm[t_idx] = buf_b_d[ib_b + t_idx]; +- } +- +- cm_result = coopmat(0); +- cm_result = coopMatMulAdd(cache_a, cache_b, cm_result); +- +- [[unroll]] for (uint col = 0; col < TN; col += storestride) { +- coopmat_stage[warp_i * TM * TN + (store_c + col) * TM + store_r] = ACC_TYPE(float(cache_a_d[store_r]) * float(cache_b_d[store_c + col])); +- } +- +- coopMatLoad(factors, coopmat_stage, warp_i * TM * TN, TM, gl_CooperativeMatrixLayoutColumnMajor); +- sums[cm_col * cms_per_row + cm_row] += factors * coopmat(cm_result); +- } +- } +-#else +- // Load from shared into cache +- [[unroll]] for (uint wsir = 0; wsir < WMITER; wsir++) { +- [[unroll]] for (uint cr = 0; cr < TM; cr++) { +- const uint ib = warp_r * WM + wsir * WSUBM + tiwr * TM + cr; +- cache_a_dm[wsir * TM + cr] = buf_a_dm[ib]; +- [[unroll]] for (uint idx_k = 0; idx_k < BK / 4; idx_k++) { +- cache_a_qs[(wsir * TM + cr) * (BK / 4) + idx_k] = buf_a_qs[ib * SHMEM_STRIDE + idx_k]; +- } +- } +- } ++ [[unroll]] for (uint wsir = 0; wsir < WMITER; wsir++) { ++ [[unroll]] for (uint cr = 0; cr < TM; cr++) { ++ const uint reg_ib = wsir * TM + cr; ++ const uint buf_ib = warp_r * WM + wsir * WSUBM + tiwr * TM + cr; + +- [[unroll]] for (uint wsic = 0; wsic < WNITER; wsic++) { +- [[unroll]] for (uint cc = 0; cc < TN; cc++) { +- const uint ib = warp_c * WN + wsic * WSUBN + tiwc * TN + cc; +- cache_b_ds[cc] = buf_b_ds[ib]; +- [[unroll]] for (uint idx_k = 0; idx_k < BK / 4; idx_k++) { +- cache_b_qs[cc * (BK / 4) + idx_k] = buf_b_qs[ib * SHMEM_STRIDE + idx_k]; ++ block_a_to_registers(reg_ib, k_step * BM + buf_ib); + } + } + +- [[unroll]] for (uint wsir = 0; wsir < WMITER; wsir++) { ++ [[unroll]] for (uint wsic = 0; wsic < WNITER; wsic++) { + [[unroll]] for (uint cc = 0; cc < TN; cc++) { +- [[unroll]] for (uint cr = 0; cr < TM; cr++) { +- const uint cache_a_idx = wsir * TM + cr; +- const uint sums_idx = (wsic * TN + cc) * (WMITER * TM) + wsir * TM + cr; +- int32_t q_sum = 0; +- [[unroll]] for (uint idx_k = 0; idx_k < BK / 4; idx_k++) { +- q_sum += dotPacked4x8EXT(cache_a_qs[cache_a_idx * (BK / 4) + idx_k], +- cache_b_qs[cc * (BK / 4) + idx_k]); +- } ++ const uint ib = k_step * BN + warp_c * WN + wsic * WSUBN + tiwc * TN + cc; ++ block_b_to_registers(ib); + +- sums[sums_idx] += mul_q8_1(q_sum, cache_a_dm[cache_a_idx], cache_b_ds[cc], 1); ++ [[unroll]] for (uint wsir = 0; wsir < WMITER; wsir++) { ++ [[unroll]] for (uint cr = 0; cr < TM; cr++) { ++ const uint cache_a_idx = wsir * TM + cr; ++ const uint sums_idx = (wsic * TN + cc) * (WMITER * TM) + wsir * TM + cr; ++ ++ sums[sums_idx] += mmq_dot_product(cache_a_idx); ++ } + } + } + } + } +-#endif + + barrier(); + } +@@ -373,54 +269,6 @@ void main() { + const uint offsets = batch_idx * p.batch_stride_d + ik * p.batch_stride_d * gl_NumWorkGroups.z; + #endif + +-#ifdef COOPMAT +-#ifdef MUL_MAT_ID +- [[unroll]] for (uint cm_row = 0; cm_row < cms_per_row; cm_row++) { +- [[unroll]] for (uint cm_col = 0; cm_col < cms_per_col; cm_col++) { +- coopMatStore(sums[cm_col * cms_per_row + cm_row], coopmat_stage, warp_i * TM * TN, TM, gl_CooperativeMatrixLayoutColumnMajor); +- +- [[unroll]] for (uint col = 0; col < BN; col += storestride) { +- const uint row_i = dc + cm_col * TN + col + store_c; +- if (row_i >= _ne1) break; +- +- const u16vec2 row_idx = row_ids[row_i]; +- +- data_d[row_idx.y * p.batch_stride_d + row_idx.x * p.stride_d + dr + cm_row * TM + store_r] = D_TYPE(coopmat_stage[warp_i * TM * TN + (col + store_c) * TM + store_r]); +- } +- } +- } +-#else +- const bool is_aligned = p.stride_d % 4 == 0; // Assumption: D_TYPE == float +- +- [[unroll]] for (uint cm_row = 0; cm_row < cms_per_row; cm_row++) { +- [[unroll]] for (uint cm_col = 0; cm_col < cms_per_col; cm_col++) { +- const bool is_in_bounds = dr + (cm_row + 1) * TM <= p.M && dc + (cm_col + 1) * TN <= p.N; +- +- if (is_aligned && is_in_bounds) { +- // Full coopMat is within bounds and stride_d is aligned with 16B +- coopmat cm_dtype = coopmat(sums[cm_col * cms_per_row + cm_row]); +- coopMatStore(cm_dtype, data_d, offsets + (dc + cm_col * TN) * p.stride_d + dr + cm_row * TM, p.stride_d, gl_CooperativeMatrixLayoutColumnMajor); +- } else if (is_in_bounds) { +- // Full coopMat is within bounds, but stride_d is not aligned +- coopMatStore(sums[cm_col * cms_per_row + cm_row], coopmat_stage, warp_i * TM * TN, TM, gl_CooperativeMatrixLayoutColumnMajor); +- +- [[unroll]] for (uint col = 0; col < TN; col += storestride) { +- data_d[offsets + (dc + cm_col * TN + col + store_c) * p.stride_d + dr + cm_row * TM + store_r] = D_TYPE(coopmat_stage[warp_i * TM * TN + (col + store_c) * TM + store_r]); +- } +- } else if (dr + cm_row * TM < p.M && dc + cm_col * TN < p.N) { +- // Partial coopMat is within bounds +- coopMatStore(sums[cm_col * cms_per_row + cm_row], coopmat_stage, warp_i * TM * TN, TM, gl_CooperativeMatrixLayoutColumnMajor); +- +- [[unroll]] for (uint col = 0; col < TN; col += storestride) { +- if (dr + cm_row * TM + store_r < p.M && dc + cm_col * TN + col + store_c < p.N) { +- data_d[offsets + (dc + cm_col * TN + col + store_c) * p.stride_d + dr + cm_row * TM + store_r] = D_TYPE(coopmat_stage[warp_i * TM * TN + (col + store_c) * TM + store_r]); +- } +- } +- } +- } +- } +-#endif // MUL_MAT_ID +-#else + [[unroll]] for (uint wsic = 0; wsic < WNITER; wsic++) { + [[unroll]] for (uint wsir = 0; wsir < WMITER; wsir++) { + +@@ -431,19 +279,21 @@ void main() { + const uint row_i = dc_warp + cc; + if (row_i >= _ne1) break; + +- const u16vec2 row_idx = row_ids[row_i]; ++ const u16vec2 row_idx = row_ids[row_i - ic * BN]; + #endif // MUL_MAT_ID + [[unroll]] for (uint cr = 0; cr < TM; cr++) { ++ const uint sums_idx = (wsic * TN + cc) * WMITER * TM + wsir * TM + cr; + #ifdef MUL_MAT_ID +- data_d[row_idx.y * p.batch_stride_d + row_idx.x * p.stride_d + dr_warp + cr] = D_TYPE(sums[(wsic * TN + cc) * (WMITER * TM) + wsir * TM + cr]); ++ if (dr_warp + cr < p.M) { ++ data_d[row_idx.y * p.batch_stride_d + row_idx.x * p.stride_d + dr_warp + cr] = D_TYPE(sums[sums_idx].x); ++ } + #else + if (dr_warp + cr < p.M && dc_warp + cc < p.N) { +- data_d[offsets + (dc_warp + cc) * p.stride_d + dr_warp + cr] = D_TYPE(sums[(wsic * TN + cc) * (WMITER * TM) + wsir * TM + cr]); ++ data_d[offsets + (dc_warp + cc) * p.stride_d + dr_warp + cr] = D_TYPE(sums[sums_idx].x); + } + #endif // MUL_MAT_ID + } + } + } + } +-#endif // COOPMAT + } +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq_funcs.glsl b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq_funcs.glsl +index fe71eb131..c0c03fedc 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq_funcs.glsl ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq_funcs.glsl +@@ -6,41 +6,89 @@ + + // Each iqs value maps to a 32-bit integer + +-#if defined(DATA_A_Q4_0) ++#if defined(DATA_A_Q4_0) || defined(DATA_A_Q4_1) ++// 2-byte loads for Q4_0 blocks (18 bytes) ++// 4-byte loads for Q4_1 blocks (20 bytes) + i32vec2 repack(uint ib, uint iqs) { +- // Use 2-byte loads since a q4_0 block (18 bytes) is not divisible by 4 +- const u16vec2 quants = u16vec2(data_a[ib].qs[iqs * 2 ], +- data_a[ib].qs[iqs * 2 + 1]); ++#ifdef DATA_A_Q4_0 ++ const u16vec2 quants = u16vec2(data_a_packed16[ib].qs[iqs * 2 ], ++ data_a_packed16[ib].qs[iqs * 2 + 1]); + const uint32_t vui = pack32(quants); + return i32vec2( vui & 0x0F0F0F0F, + (vui >> 4) & 0x0F0F0F0F); ++#else // DATA_A_Q4_1 ++ const uint32_t vui = data_a_packed32[ib].qs[iqs]; ++ return i32vec2( vui & 0x0F0F0F0F, ++ (vui >> 4) & 0x0F0F0F0F); ++#endif + } + ++#ifdef DATA_A_Q4_0 + ACC_TYPE mul_q8_1(const int32_t q_sum, const float da, const vec2 dsb, const int32_t sum_divisor) { + return ACC_TYPE(da * (float(q_sum) * dsb.x - (8 / sum_divisor) * dsb.y)); + } ++#else // DATA_A_Q4_1 ++ACC_TYPE mul_q8_1(const int32_t q_sum, const vec2 dma, const vec2 dsb, const int32_t sum_divisor) { ++ return ACC_TYPE(float(q_sum) * dma.x * dsb.x + dma.y * dsb.y / sum_divisor); ++} + #endif + +-#if defined(DATA_A_Q4_1) +-i32vec2 repack(uint ib, uint iqs) { +- // Use 4-byte loads since a q4_1 block (20 bytes) is divisible by 4 +- const uint32_t vui = data_a_packed32[ib].qs[iqs]; +- return i32vec2( vui & 0x0F0F0F0F, +- (vui >> 4) & 0x0F0F0F0F); ++#ifdef MMQ_SHMEM ++void block_a_to_shmem(const uint buf_ib, const uint ib, const uint iqs) { ++#ifdef DATA_A_Q4_0 ++ buf_a[buf_ib].qs[iqs] = pack32(u16vec2(data_a_packed16[ib].qs[iqs * 2], ++ data_a_packed16[ib].qs[iqs * 2 + 1])); ++ ++ if (iqs == 0) { ++ buf_a[buf_ib].dm = FLOAT_TYPE(data_a_packed16[ib].d); ++ } ++#else // DATA_A_Q4_1 ++ buf_a[buf_ib].qs[iqs] = data_a_packed32[ib].qs[iqs]; ++ ++ if (iqs == 0) { ++ buf_a[buf_ib].dm = FLOAT_TYPE_VEC2(data_a_packed32[ib].dm); ++ } ++#endif + } + +-ACC_TYPE mul_q8_1(const int32_t q_sum, const vec2 dma, const vec2 dsb, const int32_t sum_divisor) { +- return ACC_TYPE(float(q_sum) * dma.x * dsb.x + dma.y * dsb.y / sum_divisor); ++void block_a_to_registers(const uint reg_ib, const uint buf_ib) { ++ cache_a[reg_ib].dm = buf_a[buf_ib].dm; ++ ++ [[unroll]] for (uint iqs = 0; iqs < 4; iqs++) { ++ cache_a[reg_ib].qs[iqs] = buf_a[buf_ib].qs[iqs]; ++ } + } +-#endif + +-#if defined(DATA_A_Q5_0) ++ACC_TYPE mmq_dot_product(const uint ib_a) { ++ int32_t q_sum = 0; ++ [[unroll]] for (uint iqs = 0; iqs < 4; iqs++) { ++ const uint32_t vui = cache_a[ib_a].qs[iqs]; ++ const i32vec2 qs_a = i32vec2( vui & 0x0F0F0F0F, ++ (vui >> 4) & 0x0F0F0F0F); ++ ++ const int32_t qs_b0 = cache_b.qs[iqs]; ++ const int32_t qs_b1 = cache_b.qs[iqs + 4]; ++ ++ q_sum += dotPacked4x8EXT(qs_a.x, qs_b0); ++ q_sum += dotPacked4x8EXT(qs_a.y, qs_b1); ++ } ++ ++ return mul_q8_1(q_sum, cache_a[ib_a].dm, cache_b.ds, 1); ++} ++#endif // MMQ_SHMEM ++ ++#elif defined(DATA_A_Q5_0) || defined(DATA_A_Q5_1) ++// 2-byte loads for Q5_0 blocks (22 bytes) ++// 4-byte loads for Q5_1 blocks (24 bytes) + i32vec2 repack(uint ib, uint iqs) { +- // Use 2-byte loads since a q5_0 block (22 bytes) is not divisible by 4 +- const u16vec2 quants = u16vec2(data_a[ib].qs[iqs * 2 ], +- data_a[ib].qs[iqs * 2 + 1]); ++ const u16vec2 quants = u16vec2(data_a_packed16[ib].qs[iqs * 2 ], ++ data_a_packed16[ib].qs[iqs * 2 + 1]); + const uint32_t vui = pack32(quants); +- const int32_t qh = int32_t((uint32_t(data_a[ib].qh[1]) << 16 | data_a[ib].qh[0]) >> (4 * iqs)); ++#ifdef DATA_A_Q5_0 ++ const int32_t qh = int32_t((uint32_t(data_a_packed16[ib].qh[1]) << 16 | data_a_packed16[ib].qh[0]) >> (4 * iqs)); ++#else // DATA_A_Q5_1 ++ const int32_t qh = int32_t(data_a_packed32[ib].qh >> (4 * iqs)); ++#endif + const int32_t v0 = int32_t(vui & 0x0F0F0F0F) + | ((qh & 0xF) * 0x02040810) & 0x10101010; // (0,1,2,3) -> (4,12,20,28) + +@@ -50,40 +98,457 @@ i32vec2 repack(uint ib, uint iqs) { + return i32vec2(v0, v1); + } + ++#ifdef DATA_A_Q5_0 + ACC_TYPE mul_q8_1(const int32_t q_sum, const float da, const vec2 dsb, const int32_t sum_divisor) { + return ACC_TYPE(da * (float(q_sum) * dsb.x - (16 / sum_divisor) * dsb.y)); + } ++#else // DATA_A_Q5_1 ++ACC_TYPE mul_q8_1(const int32_t q_sum, const vec2 dma, const vec2 dsb, const int32_t sum_divisor) { ++ return ACC_TYPE(float(q_sum) * dma.x * dsb.x + dma.y * dsb.y / sum_divisor); ++} + #endif + +-#if defined(DATA_A_Q5_1) +-i32vec2 repack(uint ib, uint iqs) { +- // Use 4-byte loads since a q5_1 block (24 bytes) is divisible by 4 +- const uint32_t vui = data_a_packed32[ib].qs[iqs]; +- const int32_t qh = int32_t(data_a_packed32[ib].qh >> (4 * iqs)); +- const int32_t v0 = int32_t(vui & 0x0F0F0F0F) +- | ((qh & 0xF) * 0x02040810) & 0x10101010; // (0,1,2,3) -> (4,12,20,28) ++#ifdef MMQ_SHMEM ++void block_a_to_shmem(const uint buf_ib, const uint ib, const uint iqs) { ++#ifdef DATA_A_Q5_0 ++ buf_a[buf_ib].qs[iqs] = pack32(u16vec2(data_a_packed16[ib].qs[iqs * 2], ++ data_a_packed16[ib].qs[iqs * 2 + 1])); + +- const int32_t v1 = int32_t((vui >> 4) & 0x0F0F0F0F) +- | (((qh >> 16) & 0xF) * 0x02040810) & 0x10101010; // (16,17,18,19) -> (4,12,20,28) ++ if (iqs == 0) { ++ buf_a[buf_ib].dm = FLOAT_TYPE(data_a_packed16[ib].d); ++ buf_a[buf_ib].qh = pack32(u16vec2(data_a_packed16[ib].qh[0], data_a_packed16[ib].qh[1])); ++ } ++#else // DATA_A_Q5_1 ++ buf_a[buf_ib].qs[iqs] = data_a_packed32[ib].qs[iqs]; + +- return i32vec2(v0, v1); ++ if (iqs == 0) { ++ buf_a[buf_ib].dm = FLOAT_TYPE_VEC2(data_a_packed32[ib].dm); ++ buf_a[buf_ib].qh = data_a_packed32[ib].qh; ++ } ++#endif + } + +-ACC_TYPE mul_q8_1(const int32_t q_sum, const vec2 dma, const vec2 dsb, const int32_t sum_divisor) { +- return ACC_TYPE(float(q_sum) * dma.x * dsb.x + dma.y * dsb.y / sum_divisor); ++void block_a_to_registers(const uint reg_ib, const uint buf_ib) { ++ cache_a[reg_ib].dm = buf_a[buf_ib].dm; ++ cache_a[reg_ib].qh = buf_a[buf_ib].qh; ++ ++ [[unroll]] for (uint iqs = 0; iqs < 4; iqs++) { ++ cache_a[reg_ib].qs[iqs] = buf_a[buf_ib].qs[iqs]; ++ } + } ++ ++ACC_TYPE mmq_dot_product(const uint ib_a) { ++ int32_t q_sum = 0; ++ [[unroll]] for (uint iqs = 0; iqs < 4; iqs++) { ++ const uint32_t vui = cache_a[ib_a].qs[iqs]; ++ const int32_t qh = int32_t(cache_a[ib_a].qh >> (4 * iqs)); ++ const int32_t qs_a0 = int32_t(vui & 0x0F0F0F0F) ++ | ((qh & 0xF) * 0x02040810) & 0x10101010; // (0,1,2,3) -> (4,12,20,28) ++ const int32_t qs_a1 = int32_t((vui >> 4) & 0x0F0F0F0F) ++ | (((qh >> 16) & 0xF) * 0x02040810) & 0x10101010; // (16,17,18,19) -> (4,12,20,28) ++ ++ const int32_t qs_b0 = cache_b.qs[iqs]; ++ const int32_t qs_b1 = cache_b.qs[iqs + 4]; ++ ++ q_sum += dotPacked4x8EXT(qs_a0, qs_b0); ++ q_sum += dotPacked4x8EXT(qs_a1, qs_b1); ++ } ++ ++ return mul_q8_1(q_sum, cache_a[ib_a].dm, cache_b.ds, 1); ++} ++#endif // MMQ_SHMEM + #endif + + #if defined(DATA_A_Q8_0) ++// 2-byte loads for Q8_0 blocks (34 bytes) + int32_t repack(uint ib, uint iqs) { +- // Use 2-byte loads since a q8_0 block (34 bytes) is not divisible by 4 +- return pack32(i16vec2(data_a[ib].qs[iqs * 2 ], +- data_a[ib].qs[iqs * 2 + 1])); ++ return pack32(i16vec2(data_a_packed16[ib].qs[iqs * 2 ], ++ data_a_packed16[ib].qs[iqs * 2 + 1])); + } + + ACC_TYPE mul_q8_1(const int32_t q_sum, const float da, const vec2 dsb, const int32_t sum_divisor) { + return ACC_TYPE(float(q_sum) * da * dsb.x); + } ++ ++#ifdef MMQ_SHMEM ++void block_a_to_shmem(const uint buf_ib, const uint ib, const uint iqs) { ++ buf_a[buf_ib].qs[iqs] = pack32(i16vec2(data_a_packed16[ib].qs[iqs * 2], ++ data_a_packed16[ib].qs[iqs * 2 + 1])); ++ ++ if (iqs == 0) { ++ buf_a[buf_ib].dm = FLOAT_TYPE(data_a_packed16[ib].d); ++ } ++} ++ ++void block_a_to_registers(const uint reg_ib, const uint buf_ib) { ++ cache_a[reg_ib].dm = buf_a[buf_ib].dm; ++ ++ [[unroll]] for (uint iqs = 0; iqs < 8; iqs++) { ++ cache_a[reg_ib].qs[iqs] = buf_a[buf_ib].qs[iqs]; ++ } ++} ++ ++ACC_TYPE mmq_dot_product(const uint ib_a) { ++ int32_t q_sum = 0; ++ [[unroll]] for (uint iqs = 0; iqs < 8; iqs++) { ++ const int32_t qs_a = cache_a[ib_a].qs[iqs]; ++ const int32_t qs_b = cache_b.qs[iqs]; ++ ++ q_sum += dotPacked4x8EXT(qs_a, qs_b); ++ } ++ ++ return mul_q8_1(q_sum, cache_a[ib_a].dm, cache_b.ds, 1); ++} ++#endif // MMQ_SHMEM ++#endif ++ ++#if defined(DATA_A_MXFP4) ++// 1-byte loads for mxfp4 blocks (17 bytes) ++i32vec2 repack(uint ib, uint iqs) { ++ const uint32_t quants = pack32(u8vec4(data_a[ib].qs[iqs * 4 ], ++ data_a[ib].qs[iqs * 4 + 1], ++ data_a[ib].qs[iqs * 4 + 2], ++ data_a[ib].qs[iqs * 4 + 3])); ++ ++ return i32vec2( quants & 0x0F0F0F0F, ++ (quants >> 4) & 0x0F0F0F0F); ++} ++ ++ACC_TYPE mul_q8_1(const int32_t q_sum, const float da, const vec2 dsb, const int32_t sum_divisor) { ++ return ACC_TYPE(da * dsb.x * float(q_sum)); ++} ++ ++#ifdef MMQ_SHMEM ++void block_a_to_shmem(const uint buf_ib, const uint ib, const uint iqs) { ++ const uint32_t qs = pack32(u8vec4(data_a[ib].qs[iqs * 4 ], ++ data_a[ib].qs[iqs * 4 + 1], ++ data_a[ib].qs[iqs * 4 + 2], ++ data_a[ib].qs[iqs * 4 + 3])); ++ ++ const u8vec4 i_a0 = unpack8( qs & 0x0F0F0F0F); ++ const u8vec4 i_a1 = unpack8((qs >> 4) & 0x0F0F0F0F); ++ ++ buf_a[buf_ib].qs[iqs ] = pack32(i8vec4(kvalues_mxfp4[i_a0.x], kvalues_mxfp4[i_a0.y], kvalues_mxfp4[i_a0.z], kvalues_mxfp4[i_a0.w])); ++ buf_a[buf_ib].qs[iqs + 4] = pack32(i8vec4(kvalues_mxfp4[i_a1.x], kvalues_mxfp4[i_a1.y], kvalues_mxfp4[i_a1.z], kvalues_mxfp4[i_a1.w])); ++ ++ if (iqs == 0) { ++ buf_a[buf_ib].d = FLOAT_TYPE(e8m0_to_fp32(data_a[ib].e) * 0.5); ++ } ++} ++ ++void block_a_to_registers(const uint reg_ib, const uint buf_ib) { ++ cache_a[reg_ib].d = buf_a[buf_ib].d; ++ ++ [[unroll]] for (uint iqs = 0; iqs < 8; iqs++) { ++ cache_a[reg_ib].qs[iqs] = buf_a[buf_ib].qs[iqs]; ++ } ++} ++ ++ACC_TYPE mmq_dot_product(const uint ib_a) { ++ int32_t q_sum = 0; ++ [[unroll]] for (uint iqs = 0; iqs < 8; iqs++) { ++ const int32_t qs_a = cache_a[ib_a].qs[iqs]; ++ ++ q_sum += dotPacked4x8EXT(qs_a, cache_b.qs[iqs]); ++ } ++ ++ return mul_q8_1(q_sum, cache_a[ib_a].d, cache_b.ds, 1); ++} ++#endif // MMQ_SHMEM ++#endif ++ ++// For k-quants, ib and iqs still assume 32-wide blocks, but k-quants are 256-wide ++// iqs still refers to a 32-bit integer, meaning 0..7 for 32-wide quants ++#if defined(DATA_A_Q2_K) ++// 4-byte loads for Q2_K blocks (84 bytes) ++int32_t repack(uint ib, uint iqs) { ++ const uint ib_k = ib / 8; ++ const uint iqs_k = (ib % 8) * 8 + iqs; ++ ++ const uint qs_idx = (iqs_k / 32) * 8 + (iqs_k % 8); ++ const uint qs_shift = ((iqs_k % 32) / 8) * 2; ++ ++ return int32_t((data_a_packed32[ib_k].qs[qs_idx] >> qs_shift) & 0x03030303); ++} ++ ++uint8_t get_scale(uint ib, uint iqs) { ++ const uint ib_k = ib / 8; ++ const uint iqs_k = (ib % 8) * 8 + iqs; ++ ++ return data_a[ib_k].scales[iqs_k / 4]; ++} ++ ++ACC_TYPE mul_q8_1(const int32_t sum_d, const int32_t sum_m, const vec2 dma, const vec2 dsb, const int32_t sum_divisor) { ++ return ACC_TYPE(dsb.x * (dma.x * float(sum_d) - dma.y * float(sum_m))); ++} ++ ++#ifdef MMQ_SHMEM ++void block_a_to_shmem(const uint buf_ib, const uint ib, const uint iqs) { ++ const uint ib_k = ib / 8; ++ const uint iqs_k = (ib % 8) * 8 + iqs * QUANT_R_MMQ; ++ ++ const uint qs_idx = (iqs_k / 32) * 8 + (iqs_k % 8); ++ const uint qs_shift = ((iqs_k % 32) / 8) * 2; ++ ++ // Repack 4x4 quants into one int ++ const uint32_t vals0 = (data_a_packed32[ib_k].qs[qs_idx ] >> qs_shift) & 0x03030303; ++ const uint32_t vals1 = (data_a_packed32[ib_k].qs[qs_idx + 1] >> qs_shift) & 0x03030303; ++ const uint32_t vals2 = (data_a_packed32[ib_k].qs[qs_idx + 2] >> qs_shift) & 0x03030303; ++ const uint32_t vals3 = (data_a_packed32[ib_k].qs[qs_idx + 3] >> qs_shift) & 0x03030303; ++ ++ buf_a[buf_ib].qs[iqs] = vals0 | (vals1 << 2) | (vals2 << 4) | (vals3 << 6); ++ ++ if (iqs == 0) { ++ buf_a[buf_ib].dm = FLOAT_TYPE_VEC2(data_a_packed32[ib_k].dm); ++ buf_a[buf_ib].scales = unpack8(data_a_packed16[ib_k].scales[iqs_k / 8]); ++ } ++} ++ ++void block_a_to_registers(const uint reg_ib, const uint buf_ib) { ++ cache_a[reg_ib].dm = buf_a[buf_ib].dm; ++ cache_a[reg_ib].scales = buf_a[buf_ib].scales; ++ ++ [[unroll]] for (uint iqs = 0; iqs < 2; iqs++) { ++ cache_a[reg_ib].qs[iqs] = buf_a[buf_ib].qs[iqs]; ++ } ++} ++ ++ACC_TYPE mmq_dot_product(const uint ib_a) { ++ int32_t sum_d = 0; ++ int32_t sum_m = 0; ++ ++ [[unroll]] for (uint iqs = 0; iqs < 8; iqs++) { ++ const uint8_t scale = cache_a[ib_a].scales[iqs / 4]; ++ const int32_t scale_m = int32_t(scale >> 4) * 0x01010101; // Duplicate 8-bit value across 32-bits. ++ const int32_t qs_a = int32_t((cache_a[ib_a].qs[iqs / 4] >> ((iqs % 4) * 2)) & 0x03030303); ++ ++ sum_d += dotPacked4x8EXT(qs_a, cache_b.qs[iqs]) * (scale & 0xF); ++ sum_m += dotPacked4x8EXT(scale_m, cache_b.qs[iqs]); ++ } ++ ++ return mul_q8_1(sum_d, sum_m, cache_a[ib_a].dm, cache_b.ds, 1); ++} ++#endif // MMQ_SHMEM ++#endif ++ ++#if defined(DATA_A_Q3_K) ++// 2-byte loads for Q3_K blocks (110 bytes) ++#ifdef MMQ_SHMEM ++void block_a_to_shmem(const uint buf_ib, const uint ib, const uint iqs) { ++ const uint ib_k = ib / 8; ++ const uint hm_idx = iqs * QUANT_R_MMQ; ++ const uint iqs_k = (ib % 8) * 8 + hm_idx; ++ ++ const uint qs_idx = (iqs_k / 32) * 8 + (iqs_k % 8); ++ const uint qs_shift = ((iqs_k % 32) / 8) * 2; ++ const uint hm_shift = iqs_k / 8; ++ ++ // Repack 2x4 quants into one int ++ // Add the 3rd bit instead of subtracting it to allow packing the quants ++ const i8vec2 vals00 = unpack8(int16_t((data_a_packed16[ib_k].qs[qs_idx * 2 ] >> qs_shift) & uint16_t(0x0303))) | ++ unpack8(int16_t(((data_a_packed16[ib_k].hmask[hm_idx * 2 ] >> hm_shift) & uint16_t(0x0101)) << 2)); ++ const i8vec2 vals01 = unpack8(int16_t((data_a_packed16[ib_k].qs[qs_idx * 2 + 1 ] >> qs_shift) & uint16_t(0x0303))) | ++ unpack8(int16_t(((data_a_packed16[ib_k].hmask[hm_idx * 2 + 1] >> hm_shift) & uint16_t(0x0101)) << 2)); ++ const i8vec2 vals10 = unpack8(int16_t((data_a_packed16[ib_k].qs[qs_idx * 2 + 2 ] >> qs_shift) & uint16_t(0x0303))) | ++ unpack8(int16_t(((data_a_packed16[ib_k].hmask[hm_idx * 2 + 2] >> hm_shift) & uint16_t(0x0101)) << 2)); ++ const i8vec2 vals11 = unpack8(int16_t((data_a_packed16[ib_k].qs[qs_idx * 2 + 3 ] >> qs_shift) & uint16_t(0x0303))) | ++ unpack8(int16_t(((data_a_packed16[ib_k].hmask[hm_idx * 2 + 3] >> hm_shift) & uint16_t(0x0101)) << 2)); ++ buf_a[buf_ib].qs[iqs] = pack32(u8vec4(vals00.x, vals00.y, vals01.x, vals01.y)) | ++ (pack32(u8vec4(vals10.x, vals10.y, vals11.x, vals11.y)) << 4); ++ ++ if (iqs == 0) { ++ const uint is = iqs_k / 4; ++ const i8vec2 scales = i8vec2(unpack8(((data_a_packed16[ib_k].scales[(is % 8 ) / 2] >> (4 * (is / 8))) & 0x0F0F) | ++ (((data_a_packed16[ib_k].scales[(8 + (is % 4)) / 2] >> (2 * (is / 4))) & 0x0303) << 4))); ++ ++ buf_a[buf_ib].d_scales = FLOAT_TYPE(data_a_packed16[ib_k].d) * FLOAT_TYPE_VEC2(scales - 32); ++ } ++} ++ ++void block_a_to_registers(const uint reg_ib, const uint buf_ib) { ++ cache_a[reg_ib].d_scales = buf_a[buf_ib].d_scales; ++ ++ [[unroll]] for (uint iqs = 0; iqs < 4; iqs++) { ++ cache_a[reg_ib].qs[iqs] = buf_a[buf_ib].qs[iqs]; ++ } ++} ++ ++ACC_TYPE mmq_dot_product(const uint ib_a) { ++ float result = 0.0; ++ int32_t q_sum = 0; ++ ++ [[unroll]] for (uint iqs = 0; iqs < 4; iqs++) { ++ // Subtract 4 from the quants to correct the 3rd bit offset ++ const int32_t qs_a = pack32(unpack8(int32_t((cache_a[ib_a].qs[iqs / 2] >> ((iqs % 2) * 4)) & 0x0F0F0F0F)) - int8_t(4)); ++ ++ q_sum += dotPacked4x8EXT(qs_a, cache_b.qs[iqs]); ++ } ++ result += float(cache_a[ib_a].d_scales[0]) * float(q_sum); ++ q_sum = 0; ++ ++ [[unroll]] for (uint iqs = 4; iqs < 8; iqs++) { ++ const int32_t qs_a = pack32(unpack8(int32_t((cache_a[ib_a].qs[iqs / 2] >> ((iqs % 2) * 4)) & 0x0F0F0F0F)) - int8_t(4)); ++ ++ q_sum += dotPacked4x8EXT(qs_a, cache_b.qs[iqs]); ++ } ++ result += float(cache_a[ib_a].d_scales[1]) * float(q_sum); ++ ++ return ACC_TYPE(cache_b.ds.x * result); ++} ++#endif // MMQ_SHMEM ++#endif ++ ++#if defined(DATA_A_Q4_K) || defined(DATA_A_Q5_K) ++// 4-byte loads for Q4_K blocks (144 bytes) and Q5_K blocks (176 bytes) ++ACC_TYPE mul_q8_1(const int32_t q_sum, const vec2 dma, const vec2 dsb, const int32_t sum_divisor) { ++ return ACC_TYPE(dsb.x * dma.x * float(q_sum) - dma.y * dsb.y); ++} ++ ++#ifdef MMQ_SHMEM ++void block_a_to_shmem(const uint buf_ib, const uint ib, const uint iqs) { ++ const uint ib_k = ib / 8; ++ const uint iqs_k = (ib % 8) * 8 + iqs * QUANT_R_MMQ; ++ ++ const uint qs_idx = (iqs_k / 16) * 8 + (iqs_k % 8); ++ const uint qs_shift = ((iqs_k % 16) / 8) * 4; ++ ++ // Repack 2x4 quants into one int ++#if defined(DATA_A_Q4_K) ++ const uint32_t vals0 = (data_a_packed32[ib_k].qs[qs_idx ] >> qs_shift) & 0x0F0F0F0F; ++ const uint32_t vals1 = (data_a_packed32[ib_k].qs[qs_idx + 1] >> qs_shift) & 0x0F0F0F0F; ++ ++ buf_a[buf_ib].qs[iqs] = vals0 | (vals1 << 4); ++#else // defined(DATA_A_Q5_K) ++ const uint qh_idx = iqs * QUANT_R_MMQ; ++ const uint qh_shift = iqs_k / 8; ++ ++ buf_a[buf_ib].qs[iqs] = int32_t(((data_a_packed32[ib_k].qs[qs_idx] >> qs_shift) & 0x0F0F0F0F) | ++ (((data_a_packed32[ib_k].qh[qh_idx] >> qh_shift) & 0x01010101) << 4)); ++#endif ++ ++ ++ if (iqs == 0) { ++ // Scale index ++ const uint is = iqs_k / 8; ++ u8vec2 scale_dm; ++ if (is < 4) { ++ scale_dm = u8vec2(data_a[ib_k].scales[is] & 0x3F, data_a[ib_k].scales[is + 4] & 0x3F); ++ } else { ++ scale_dm = u8vec2((data_a[ib_k].scales[is+4] & 0xF) | ((data_a[ib_k].scales[is-4] & 0xC0) >> 2), ++ (data_a[ib_k].scales[is+4] >> 4) | ((data_a[ib_k].scales[is ] & 0xC0) >> 2)); ++ } ++ ++ buf_a[buf_ib].dm = FLOAT_TYPE_VEC2(data_a_packed32[ib_k].dm) * FLOAT_TYPE_VEC2(scale_dm); ++ } ++} ++ ++void block_a_to_registers(const uint reg_ib, const uint buf_ib) { ++ cache_a[reg_ib].dm = buf_a[buf_ib].dm; ++ ++ [[unroll]] for (uint iqs = 0; iqs < 8 / QUANT_R_MMQ; iqs++) { ++ cache_a[reg_ib].qs[iqs] = buf_a[buf_ib].qs[iqs]; ++ } ++} ++ ++ACC_TYPE mmq_dot_product(const uint ib_a) { ++ int32_t q_sum = 0; ++ ++ [[unroll]] for (uint iqs = 0; iqs < 8; iqs++) { ++#if defined(DATA_A_Q4_K) ++ const int32_t qs_a = int32_t((cache_a[ib_a].qs[iqs / 2] >> ((iqs % 2) * 4)) & 0x0F0F0F0F); ++#else // defined(DATA_A_Q5_K) ++ const int32_t qs_a = cache_a[ib_a].qs[iqs]; ++#endif ++ ++ q_sum += dotPacked4x8EXT(qs_a, cache_b.qs[iqs]); ++ } ++ ++ return mul_q8_1(q_sum, cache_a[ib_a].dm, cache_b.ds, 1); ++} ++#endif // MMQ_SHMEM ++#endif ++ ++#ifdef MMQ_SHMEM ++void block_b_to_shmem(const uint buf_ib, const uint ib, const uint iqs) { ++ const uint ib_outer = ib / 4; ++ const uint ib_inner = ib % 4; ++ ++ if (iqs == 0) { ++ buf_b[buf_ib].ds = FLOAT_TYPE_VEC2(data_b[ib_outer].ds[ib_inner]); ++ } ++ ++ const ivec4 values = data_b[ib_outer].qs[ib_inner * 2 + iqs]; ++ buf_b[buf_ib].qs[iqs * 4 ] = values.x; ++ buf_b[buf_ib].qs[iqs * 4 + 1] = values.y; ++ buf_b[buf_ib].qs[iqs * 4 + 2] = values.z; ++ buf_b[buf_ib].qs[iqs * 4 + 3] = values.w; ++} ++ ++void block_b_to_registers(const uint ib) { ++ cache_b.ds = buf_b[ib].ds; ++ [[unroll]] for (uint iqs = 0; iqs < BK / 4; iqs++) { ++ cache_b.qs[iqs] = buf_b[ib].qs[iqs]; ++ } ++} ++#endif ++ ++#if defined(DATA_A_Q6_K) ++// 2-byte loads for Q6_K blocks (210 bytes) ++#ifdef MMQ_SHMEM ++void block_a_to_shmem(const uint buf_ib, const uint ib, const uint iqs) { ++ const uint ib_k = ib / 8; ++ const uint iqs_k = (ib % 8) * 8 + iqs; ++ ++ const uint ql_idx = (iqs_k / 32) * 16 + iqs_k % 16; ++ const uint ql_shift = ((iqs_k % 32) / 16) * 4; ++ ++ const uint qh_idx = (iqs_k / 32) * 8 + iqs; ++ const uint qh_shift = ((iqs_k % 32) / 8) * 2; ++ ++ const i8vec2 vals00 = (unpack8(int16_t((data_a_packed16[ib_k].ql[ql_idx * 2 ] >> ql_shift) & uint16_t(0x0F0F))) | ++ unpack8(int16_t(((data_a_packed16[ib_k].qh[qh_idx * 2 ] >> qh_shift) & uint16_t(0x0303)) << 4))) - int8_t(32); ++ const i8vec2 vals01 = (unpack8(int16_t((data_a_packed16[ib_k].ql[ql_idx * 2 + 1] >> ql_shift) & uint16_t(0x0F0F))) | ++ unpack8(int16_t(((data_a_packed16[ib_k].qh[qh_idx * 2 + 1] >> qh_shift) & uint16_t(0x0303)) << 4))) - int8_t(32); ++ buf_a[buf_ib].qs[iqs] = pack32(i8vec4(vals00.x, vals00.y, vals01.x, vals01.y)); ++ ++ if (iqs == 0) { ++ const uint is = iqs_k / 4; ++ const i8vec2 scales = unpack8(data_a_packed16[ib_k].scales[is / 2]); ++ ++ buf_a[buf_ib].d_scales = FLOAT_TYPE(data_a_packed16[ib_k].d) * FLOAT_TYPE_VEC2(scales); ++ } ++} ++ ++void block_a_to_registers(const uint reg_ib, const uint buf_ib) { ++ cache_a[reg_ib].d_scales = buf_a[buf_ib].d_scales; ++ ++ [[unroll]] for (uint iqs = 0; iqs < 8; iqs++) { ++ cache_a[reg_ib].qs[iqs] = buf_a[buf_ib].qs[iqs]; ++ } ++} ++ ++ACC_TYPE mmq_dot_product(const uint ib_a) { ++ float result = 0.0; ++ int32_t q_sum = 0; ++ ++ [[unroll]] for (uint iqs = 0; iqs < 4; iqs++) { ++ const int32_t qs_a = cache_a[ib_a].qs[iqs]; ++ ++ q_sum += dotPacked4x8EXT(qs_a, cache_b.qs[iqs]); ++ } ++ result += float(cache_a[ib_a].d_scales[0]) * float(q_sum); ++ q_sum = 0; ++ ++ [[unroll]] for (uint iqs = 4; iqs < 8; iqs++) { ++ const int32_t qs_a = cache_a[ib_a].qs[iqs]; ++ ++ q_sum += dotPacked4x8EXT(qs_a, cache_b.qs[iqs]); ++ } ++ result += float(cache_a[ib_a].d_scales[1]) * float(q_sum); ++ ++ return ACC_TYPE(cache_b.ds.x * result); ++} ++#endif // MMQ_SHMEM + #endif + + #if defined(DATA_A_Q4_0) || defined(DATA_A_Q5_0) || defined(DATA_A_Q8_0) || defined(DATA_A_IQ1_S) || defined(DATA_A_IQ2_XXS) || defined(DATA_A_IQ2_XS) || defined(DATA_A_IQ2_S) || defined(DATA_A_IQ3_XXS) || defined(DATA_A_IQ3_S) || defined(DATA_A_IQ4_XS) || defined(DATA_A_IQ4_NL) +@@ -103,3 +568,10 @@ FLOAT_TYPE_VEC2 get_dm(uint ib) { + return FLOAT_TYPE_VEC2(data_a_packed32[ib].dm); + } + #endif ++ ++#if defined(DATA_A_Q2_K) ++FLOAT_TYPE_VEC2 get_dm(uint ib) { ++ const uint ib_k = ib / 8; ++ return FLOAT_TYPE_VEC2(data_a_packed32[ib_k].dm); ++} ++#endif +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq_shmem_types.glsl b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq_shmem_types.glsl +new file mode 100644 +index 000000000..72fec4404 +--- /dev/null ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq_shmem_types.glsl +@@ -0,0 +1,78 @@ ++#if defined(DATA_A_Q4_0) ++#define QUANT_R_MMQ 2 ++struct block_a_cache { ++ uint32_t qs[16/4]; ++ FLOAT_TYPE dm; ++}; ++#elif defined(DATA_A_Q4_1) ++#define QUANT_R_MMQ 2 ++struct block_a_cache { ++ uint32_t qs[16/4]; ++ FLOAT_TYPE_VEC2 dm; ++}; ++#elif defined(DATA_A_Q5_0) ++#define QUANT_R_MMQ 2 ++struct block_a_cache { ++ uint32_t qs[16/4]; ++ uint32_t qh; ++ FLOAT_TYPE dm; ++}; ++#elif defined(DATA_A_Q5_1) ++#define QUANT_R_MMQ 2 ++struct block_a_cache { ++ uint32_t qs[16/4]; ++ uint32_t qh; ++ FLOAT_TYPE_VEC2 dm; ++}; ++#elif defined(DATA_A_Q8_0) ++#define QUANT_R_MMQ 1 ++// AMD likes 4, Intel likes 1 and Nvidia likes 2 ++#define BK_STEP 1 ++struct block_a_cache { ++ int32_t qs[32/4]; ++ FLOAT_TYPE dm; ++}; ++#elif defined(DATA_A_MXFP4) ++#define QUANT_R_MMQ 2 ++struct block_a_cache { ++ int32_t qs[8]; ++ FLOAT_TYPE d; ++}; ++#elif defined(DATA_A_Q2_K) ++#define QUANT_R_MMQ 4 ++struct block_a_cache { ++ uint32_t qs[2]; ++ u8vec2 scales; ++ FLOAT_TYPE_VEC2 dm; ++}; ++#elif defined(DATA_A_Q3_K) ++#define QUANT_R_MMQ 2 ++struct block_a_cache { ++ uint32_t qs[4]; ++ FLOAT_TYPE_VEC2 d_scales; ++}; ++#elif defined(DATA_A_Q4_K) ++#define QUANT_R_MMQ 2 ++struct block_a_cache { ++ uint32_t qs[4]; ++ FLOAT_TYPE_VEC2 dm; ++}; ++#elif defined(DATA_A_Q5_K) ++#define QUANT_R_MMQ 1 ++struct block_a_cache { ++ int32_t qs[8]; ++ FLOAT_TYPE_VEC2 dm; ++}; ++#elif defined(DATA_A_Q6_K) ++#define QUANT_R_MMQ 1 ++struct block_a_cache { ++ int32_t qs[8]; ++ FLOAT_TYPE_VEC2 d_scales; ++}; ++#endif ++ ++struct block_b_cache ++{ ++ int32_t qs[8]; ++ FLOAT_TYPE_VEC2 ds; ++}; +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/types.glsl b/ggml/src/ggml-vulkan/vulkan-shaders/types.glsl +index 2fa54ce51..02578c77c 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/types.glsl ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/types.glsl +@@ -66,6 +66,7 @@ struct block_q4_0_packed16 + #define QUANT_AUXF 1 + #define A_TYPE block_q4_0 + #define A_TYPE_PACKED16 block_q4_0_packed16 ++#define DATA_A_QUANT_LEGACY + #endif + + #define QUANT_K_Q4_1 32 +@@ -98,6 +99,7 @@ struct block_q4_1_packed32 + #define A_TYPE block_q4_1 + #define A_TYPE_PACKED16 block_q4_1_packed16 + #define A_TYPE_PACKED32 block_q4_1_packed32 ++#define DATA_A_QUANT_LEGACY + #endif + + #define QUANT_K_Q5_0 32 +@@ -123,6 +125,7 @@ struct block_q5_0_packed16 + #define QUANT_AUXF 1 + #define A_TYPE block_q5_0 + #define A_TYPE_PACKED16 block_q5_0_packed16 ++#define DATA_A_QUANT_LEGACY + #endif + + #define QUANT_K_Q5_1 32 +@@ -158,6 +161,7 @@ struct block_q5_1_packed32 + #define A_TYPE block_q5_1 + #define A_TYPE_PACKED16 block_q5_1_packed16 + #define A_TYPE_PACKED32 block_q5_1_packed32 ++#define DATA_A_QUANT_LEGACY + #endif + + #define QUANT_K_Q8_0 32 +@@ -186,6 +190,7 @@ struct block_q8_0_packed32 + #define A_TYPE block_q8_0 + #define A_TYPE_PACKED16 block_q8_0_packed16 + #define A_TYPE_PACKED32 block_q8_0_packed32 ++#define DATA_A_QUANT_LEGACY + #endif + + #define QUANT_K_Q8_1 32 +@@ -226,21 +231,21 @@ struct block_q2_K + { + uint8_t scales[QUANT_K_Q2_K/16]; + uint8_t qs[QUANT_K_Q2_K/4]; +- f16vec2 d; ++ f16vec2 dm; + }; + + struct block_q2_K_packed16 + { + uint16_t scales[QUANT_K_Q2_K/16/2]; + uint16_t qs[QUANT_K_Q2_K/4/2]; +- f16vec2 d; ++ f16vec2 dm; + }; + + struct block_q2_K_packed32 + { + uint32_t scales[QUANT_K_Q2_K/16/4]; + uint32_t qs[QUANT_K_Q2_K/4/4]; +- f16vec2 d; ++ f16vec2 dm; + }; + + #if defined(DATA_A_Q2_K) +@@ -249,6 +254,8 @@ struct block_q2_K_packed32 + #define A_TYPE block_q2_K + #define A_TYPE_PACKED16 block_q2_K_packed16 + #define A_TYPE_PACKED32 block_q2_K_packed32 ++#define SCALES_PER_32 2 ++#define DATA_A_QUANT_K + #endif + + #define QUANT_K_Q3_K 256 +@@ -274,27 +281,28 @@ struct block_q3_K_packed16 + #define QUANT_R 1 + #define A_TYPE block_q3_K + #define A_TYPE_PACKED16 block_q3_K_packed16 ++#define DATA_A_QUANT_K + #endif + + #define QUANT_K_Q4_K 256 + + struct block_q4_K + { +- f16vec2 d; ++ f16vec2 dm; + uint8_t scales[3*QUANT_K_Q4_K/64]; + uint8_t qs[QUANT_K_Q4_K/2]; + }; + + struct block_q4_K_packed16 + { +- f16vec2 d; ++ f16vec2 dm; + uint16_t scales[3*QUANT_K_Q4_K/64/2]; + uint16_t qs[QUANT_K_Q4_K/2/2]; + }; + + struct block_q4_K_packed32 + { +- f16vec2 d; ++ f16vec2 dm; + uint32_t scales[3*QUANT_K_Q4_K/64/4]; + uint32_t qs[QUANT_K_Q4_K/2/4]; + }; +@@ -310,13 +318,14 @@ struct block_q4_K_packed128 + #define A_TYPE block_q4_K + #define A_TYPE_PACKED16 block_q4_K_packed16 + #define A_TYPE_PACKED32 block_q4_K_packed32 ++#define DATA_A_QUANT_K + #endif + + #define QUANT_K_Q5_K 256 + + struct block_q5_K + { +- f16vec2 d; ++ f16vec2 dm; + uint8_t scales[12]; + uint8_t qh[QUANT_K_Q5_K/8]; + uint8_t qs[QUANT_K_Q5_K/2]; +@@ -324,12 +333,20 @@ struct block_q5_K + + struct block_q5_K_packed16 + { +- f16vec2 d; ++ f16vec2 dm; + uint16_t scales[12/2]; + uint16_t qh[QUANT_K_Q5_K/8/2]; + uint16_t qs[QUANT_K_Q5_K/2/2]; + }; + ++struct block_q5_K_packed32 ++{ ++ f16vec2 dm; ++ uint32_t scales[12/4]; ++ uint32_t qh[QUANT_K_Q5_K/8/4]; ++ uint32_t qs[QUANT_K_Q5_K/2/4]; ++}; ++ + struct block_q5_K_packed128 + { + uvec4 q5k[11]; +@@ -340,6 +357,8 @@ struct block_q5_K_packed128 + #define QUANT_R 1 + #define A_TYPE block_q5_K + #define A_TYPE_PACKED16 block_q5_K_packed16 ++#define A_TYPE_PACKED32 block_q5_K_packed32 ++#define DATA_A_QUANT_K + #endif + + #define QUANT_K_Q6_K 256 +@@ -356,7 +375,7 @@ struct block_q6_K_packed16 + { + uint16_t ql[QUANT_K_Q6_K/2/2]; + uint16_t qh[QUANT_K_Q6_K/4/2]; +- int8_t scales[QUANT_K_Q6_K/16]; ++ int16_t scales[QUANT_K_Q6_K/16/2]; + float16_t d; + }; + +@@ -365,6 +384,7 @@ struct block_q6_K_packed16 + #define QUANT_R 1 + #define A_TYPE block_q6_K + #define A_TYPE_PACKED16 block_q6_K_packed16 ++#define DATA_A_QUANT_K + #endif + + // IQuants +@@ -1363,18 +1383,11 @@ struct block_mxfp4 + uint8_t qs[QUANT_K_MXFP4/2]; + }; + +-//struct block_mxfp4_packed16 +-//{ +-// uint8_t e; +-// uint16_t qs[QUANT_K_MXFP4/2/2]; +-//}; +- + #if defined(DATA_A_MXFP4) + #define QUANT_K QUANT_K_MXFP4 + #define QUANT_R QUANT_R_MXFP4 + #define QUANT_AUXF 1 + #define A_TYPE block_mxfp4 +-//#define A_TYPE_PACKED16 block_mxfp4_packed16 + #endif + + #if defined(DATA_A_IQ4_NL) || defined(DATA_A_IQ4_XS) +@@ -1397,12 +1410,12 @@ void init_iq_shmem(uvec3 wgsize) + #endif + + #if defined(DATA_A_MXFP4) +-const FLOAT_TYPE kvalues_mxfp4_const[16] = { +- FLOAT_TYPE(0.0f), FLOAT_TYPE(0.5f), FLOAT_TYPE(1.0f), FLOAT_TYPE(1.5f), FLOAT_TYPE(2.0f), FLOAT_TYPE(3.0f), FLOAT_TYPE(4.0f), FLOAT_TYPE(6.0f), +- FLOAT_TYPE(-0.0f), FLOAT_TYPE(-0.5f), FLOAT_TYPE(-1.0f), FLOAT_TYPE(-1.5f), FLOAT_TYPE(-2.0f), FLOAT_TYPE(-3.0f), FLOAT_TYPE(-4.0f), FLOAT_TYPE(-6.0f) ++const int8_t kvalues_mxfp4_const[16] = { ++ int8_t(0), int8_t(1), int8_t(2), int8_t(3), int8_t(4), int8_t(6), int8_t(8), int8_t(12), ++ int8_t(0), int8_t(-1), int8_t(-2), int8_t(-3), int8_t(-4), int8_t(-6), int8_t(-8), int8_t(-12), + }; + +-shared FLOAT_TYPE kvalues_mxfp4[16]; ++shared int8_t kvalues_mxfp4[16]; + + #define NEEDS_INIT_IQ_SHMEM + void init_iq_shmem(uvec3 wgsize) +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/vulkan-shaders-gen.cpp b/ggml/src/ggml-vulkan/vulkan-shaders/vulkan-shaders-gen.cpp +index 0f25ba345..03fa01639 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/vulkan-shaders-gen.cpp ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/vulkan-shaders-gen.cpp +@@ -566,7 +566,8 @@ void matmul_shaders(bool fp16, MatMulIdType matmul_id_type, bool coopmat, bool c + } + + #if defined(GGML_VULKAN_INTEGER_DOT_GLSLC_SUPPORT) +- if (!coopmat && !coopmat2 && matmul_id_type == MatMulIdType::NONE && is_legacy_quant(tname)) { ++ // Integer dot mmq performs better with f32 accumulators ++ if (!f16acc && !coopmat && !coopmat2 && (is_legacy_quant(tname) || is_k_quant(tname) || tname == "mxfp4")) { + string_to_spv(shader_name + "_" + tname + "_q8_1", "mul_mmq.comp", merge_maps(merge_maps(base_dict, float_type_dict), {{data_a_key, "1"}, {"D_TYPE", "float"},}), fp16, coopmat, coopmat2, f16acc); + } + #endif +@@ -574,7 +575,7 @@ void matmul_shaders(bool fp16, MatMulIdType matmul_id_type, bool coopmat, bool c + } + + void process_shaders() { +- std::map base_dict = {{"FLOAT_TYPE", "float"}}; ++ std::map base_dict = {{"FLOAT_TYPE", "float"}, {"FLOAT_TYPE_VEC2", "vec2"}}; + + // matmul + for (const MatMulIdType& matmul_id_type : {MatMulIdType::NONE, MatMulIdType::DEFAULT, MatMulIdType::SUBGROUP}) { diff --git a/llama/patches/0031-vulkan-Update-topk_moe-fusion-to-handle-gpt-s-late-s.patch b/llama/patches/0031-vulkan-Update-topk_moe-fusion-to-handle-gpt-s-late-s.patch new file mode 100644 index 00000000..41cd9cd5 --- /dev/null +++ b/llama/patches/0031-vulkan-Update-topk_moe-fusion-to-handle-gpt-s-late-s.patch @@ -0,0 +1,657 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jeff Bolz +Date: Wed, 29 Oct 2025 08:44:29 -0500 +Subject: [PATCH] vulkan: Update topk_moe fusion to handle gpt's late softmax + (#16656) + +* vulkan: Update topk_moe fusion to handle gpt's late softmax + +Based on #16649. + +* Add ggml_check_edges + +* Add sync logging to show fusion effects + +* handle clamp added in #16655 + +* Update ggml/src/ggml-impl.h + +Co-authored-by: Diego Devesa +--- + ggml/src/ggml-impl.h | 16 + + ggml/src/ggml-vulkan/ggml-vulkan.cpp | 304 +++++++++++------- + .../ggml-vulkan/vulkan-shaders/topk_moe.comp | 90 ++++-- + 3 files changed, 272 insertions(+), 138 deletions(-) + +diff --git a/ggml/src/ggml-impl.h b/ggml/src/ggml-impl.h +index 639d551a2..e5c446d1d 100644 +--- a/ggml/src/ggml-impl.h ++++ b/ggml/src/ggml-impl.h +@@ -693,6 +693,7 @@ GGML_API void ggml_dxgi_pdh_release(); + #endif + + #ifdef __cplusplus ++#include + #include + #include + +@@ -708,6 +709,21 @@ inline bool ggml_can_fuse_subgraph(const struct ggml_cgraph * cgraph, + return ggml_can_fuse_subgraph(cgraph, start_idx, ops.size(), ops.begin(), outputs.begin(), outputs.size()); + } + ++// Return true if the edges in the graph match expectations. ++inline bool ggml_check_edges(const struct ggml_cgraph * cgraph, ++ int start_idx, ++ std::initializer_list> edges) { ++ for (const auto & edge : edges) { ++ int dst_node = edge[0]; ++ int src_idx = edge[1]; ++ int src_node = edge[2]; ++ if (cgraph->nodes[start_idx + dst_node]->src[src_idx] != cgraph->nodes[start_idx + src_node]) { ++ return false; ++ } ++ } ++ return true; ++} ++ + // expose GGUF internals for test code + GGML_API size_t gguf_type_size(enum gguf_type type); + GGML_API struct gguf_context * gguf_init_from_file_impl(FILE * file, struct gguf_init_params params); +diff --git a/ggml/src/ggml-vulkan/ggml-vulkan.cpp b/ggml/src/ggml-vulkan/ggml-vulkan.cpp +index 53b57c179..b2855b078 100644 +--- a/ggml/src/ggml-vulkan/ggml-vulkan.cpp ++++ b/ggml/src/ggml-vulkan/ggml-vulkan.cpp +@@ -387,12 +387,76 @@ static constexpr uint32_t num_argsort_pipelines = 11; + static constexpr uint32_t max_argsort_cols = 1 << (num_argsort_pipelines-1); + static constexpr uint32_t num_topk_moe_pipelines = 10; + +-static constexpr std::array topk_moe_norm{ GGML_OP_SOFT_MAX, GGML_OP_RESHAPE, GGML_OP_ARGSORT, +- GGML_OP_VIEW, GGML_OP_GET_ROWS, GGML_OP_RESHAPE, +- GGML_OP_SUM_ROWS, GGML_OP_DIV, GGML_OP_RESHAPE }; +-static constexpr std::array topk_moe { GGML_OP_SOFT_MAX, GGML_OP_RESHAPE, GGML_OP_ARGSORT, +- GGML_OP_VIEW, GGML_OP_GET_ROWS }; ++static constexpr std::initializer_list topk_moe_early_softmax_norm{ GGML_OP_SOFT_MAX, GGML_OP_RESHAPE, GGML_OP_ARGSORT, ++ GGML_OP_VIEW, GGML_OP_GET_ROWS, GGML_OP_RESHAPE, ++ GGML_OP_SUM_ROWS, GGML_OP_CLAMP, GGML_OP_DIV, ++ GGML_OP_RESHAPE }; ++static constexpr std::initializer_list topk_moe_early_softmax { GGML_OP_SOFT_MAX, GGML_OP_RESHAPE, GGML_OP_ARGSORT, ++ GGML_OP_VIEW, GGML_OP_GET_ROWS }; ++static constexpr std::initializer_list topk_moe_late_softmax { GGML_OP_ARGSORT, GGML_OP_VIEW, ++ GGML_OP_GET_ROWS, GGML_OP_RESHAPE, ++ GGML_OP_SOFT_MAX, GGML_OP_RESHAPE }; ++ ++//node #978 ( SOFT_MAX): ffn_moe_probs-15 ( 0K) [Vulka ] use=2: ffn_moe_logits-15 ( 0K) [Vulka ] ++//node #979 ( RESHAPE): ffn_moe_probs-15 (re ( 0K) [Vulka ] use=1: ffn_moe_probs-15 ( 0K) [Vulka ] ++//node #980 ( ARGSORT): ffn_moe_argsort-15 ( 0K) [Vulka ] use=1: ffn_moe_probs-15 ( 0K) [Vulka ] ++//node #981 ( VIEW): ffn_moe_topk-15 ( 0K) [Vulka ] use=4: ffn_moe_argsort-15 ( 0K) [Vulka ] ++//node #982 ( GET_ROWS): ffn_moe_weights-15 ( 0K) [Vulka ] use=1: ffn_moe_probs-15 (re ( 0K) [Vulka ] ffn_moe_topk-15 ( 0K) [Vulka ] ++//node #983 ( RESHAPE): ffn_moe_weights-15 ( ( 0K) [Vulka ] use=2: ffn_moe_weights-15 ( 0K) [Vulka ] ++//node #984 ( SUM_ROWS): ffn_moe_weights_sum- ( 0K) [Vulka ] use=1: ffn_moe_weights-15 ( ( 0K) [Vulka ] ++//node #985 ( CLAMP): ffn_moe_weights_sum_ ( 0K) [Vulka ] use=1: ffn_moe_weights_sum- ( 0K) [Vulka ] ++//node #986 ( DIV): ffn_moe_weights_norm ( 0K) [Vulka ] use=1: ffn_moe_weights-15 ( ( 0K) [Vulka ] ffn_moe_weights_sum_ ( 0K) [Vulka ] ++//node #987 ( RESHAPE): ffn_moe_weights_norm ( 0K) [Vulka ] use=1: ffn_moe_weights_norm ( 0K) [Vulka ] ++static constexpr std::initializer_list> topk_moe_early_softmax_norm_edges { ++ { 1, 0, 0 }, // reshape->src[0] == softmax ++ { 2, 0, 0 }, // argsort->src[0] == softmax ++ { 3, 0, 2 }, // view->src[0] == argsort ++ { 4, 0, 1 }, // get_rows->src[0] == reshape ++ { 4, 1, 3 }, // get_rows->src[1] == view ++ { 5, 0, 4 }, // reshape->src[0] == get_rows ++ { 6, 0, 5 }, // sum_rows->src[0] == reshape ++ { 7, 0, 6 }, // clamp->src[0] == sum_rows ++ { 8, 0, 5 }, // div->src[0] == reshape ++ { 8, 1, 7 }, // div->src[1] == clamp ++ { 9, 0, 8 }, // reshape->src[0] == div ++}; ++ ++// same as early_softmax_norm but ending after the get_rows ++static constexpr std::initializer_list> topk_moe_early_softmax_edges { ++ { 1, 0, 0 }, // reshape->src[0] == softmax ++ { 2, 0, 0 }, // argsort->src[0] == softmax ++ { 3, 0, 2 }, // view->src[0] == argsort ++ { 4, 0, 1 }, // get_rows->src[0] == reshape ++ { 4, 1, 3 }, // get_rows->src[1] == view ++}; + ++//node #652 ( ARGSORT): ffn_moe_argsort-11 ( 0K) [Vulka ] use=1: ffn_moe_probs-11 ( 0K) [Vulka ] ++//node #653 ( VIEW): ffn_moe_topk-11 ( 0K) [Vulka ] use=7: ffn_moe_argsort-11 ( 0K) [Vulka ] ++//node #654 ( GET_ROWS): ffn_moe_weights-11 ( 0K) [Vulka ] use=1: ffn_moe_probs-11 (re ( 0K) [Vulka ] ffn_moe_topk-11 ( 0K) [Vulka ] ++//node #655 ( RESHAPE): ffn_moe_weights-11 ( ( 0K) [Vulka ] use=1: ffn_moe_weights-11 ( 0K) [Vulka ] ++//node #656 ( SOFT_MAX): node_656 ( 0K) [Vulka ] use=1: ffn_moe_weights-11 ( ( 0K) [Vulka ] ++//node #657 ( RESHAPE): ffn_moe_weights_soft ( 0K) [Vulka ] use=1: node_656 ( 0K) [Vulka ] ++static constexpr std::initializer_list> topk_moe_late_softmax_edges { ++ { 1, 0, 0 }, // view->src[0] == argsort ++ { 2, 1, 1 }, // get_rows->src[1] == view ++ { 3, 0, 2 }, // reshape->src[0] == get_rows ++ { 4, 0, 3 }, // soft_max->src[0] == reshape ++ { 5, 0, 4 }, // reshape->src[0] == soft_max ++}; ++ ++enum topk_moe_mode { ++ TOPK_MOE_EARLY_SOFTMAX, ++ TOPK_MOE_EARLY_SOFTMAX_NORM, ++ TOPK_MOE_LATE_SOFTMAX, ++ TOPK_MOE_COUNT, ++}; ++ ++static topk_moe_mode ggml_vk_num_additional_ops_to_topk_moe_mode(uint32_t num) { ++ topk_moe_mode mode = num == topk_moe_early_softmax_norm.size() - 1 ? TOPK_MOE_EARLY_SOFTMAX_NORM : ++ num == topk_moe_early_softmax.size() - 1 ? TOPK_MOE_EARLY_SOFTMAX : ++ TOPK_MOE_LATE_SOFTMAX; ++ return mode; ++} + + struct vk_device_struct { + std::recursive_mutex mutex; +@@ -607,8 +671,7 @@ struct vk_device_struct { + + vk_pipeline pipeline_flash_attn_split_k_reduce; + +- // [2] is {!norm, norm} +- vk_pipeline pipeline_topk_moe[num_topk_moe_pipelines][2]; ++ vk_pipeline pipeline_topk_moe[num_topk_moe_pipelines][TOPK_MOE_COUNT]; + + std::vector all_pipelines; + +@@ -956,6 +1019,8 @@ static_assert(sizeof(vk_op_multi_add_push_constants) <= 256); + struct vk_op_topk_moe_push_constants { + uint32_t n_rows; + uint32_t n_expert_used; ++ float clamp_min; ++ float clamp_max; + }; + + struct vk_op_add_id_push_constants { +@@ -3806,8 +3871,9 @@ static void ggml_vk_load_shaders(vk_device& device) { + ggml_vk_create_pipeline(device, device->pipeline_conv2d_dw_cwhn_f16_f32, "conv2d_dw_cwhn_f16_f32", conv2d_dw_cwhn_f16_f32_len, conv2d_dw_cwhn_f16_f32_data, "main", 3, sizeof(vk_op_conv2d_dw_push_constants), {512, 1, 1}, {}, 1); + + for (uint32_t i = 0; i < num_topk_moe_pipelines; ++i) { +- ggml_vk_create_pipeline2(device, device->pipeline_topk_moe[i][0], "topk_moe_f32_"+std::to_string(i), topk_moe_f32_len, topk_moe_f32_data, "main", 3, sizeof(vk_op_topk_moe_push_constants), {1, 1, 1}, {device->subgroup_size, 1u<pipeline_topk_moe[i][1], "topk_moe_f32_"+std::to_string(i), topk_moe_f32_len, topk_moe_f32_data, "main", 3, sizeof(vk_op_topk_moe_push_constants), {1, 1, 1}, {device->subgroup_size, 1u<pipeline_topk_moe[i][TOPK_MOE_EARLY_SOFTMAX], "topk_moe_f32_early_softmax_"+std::to_string(i), topk_moe_f32_len, topk_moe_f32_data, "main", 3, sizeof(vk_op_topk_moe_push_constants), {1, 1, 1}, {device->subgroup_size, 1u<pipeline_topk_moe[i][TOPK_MOE_EARLY_SOFTMAX_NORM], "topk_moe_f32_early_softmax_norm"+std::to_string(i), topk_moe_f32_len, topk_moe_f32_data, "main", 3, sizeof(vk_op_topk_moe_push_constants), {1, 1, 1}, {device->subgroup_size, 1u<pipeline_topk_moe[i][TOPK_MOE_LATE_SOFTMAX], "topk_moe_f32_late_softmax"+std::to_string(i), topk_moe_f32_len, topk_moe_f32_data, "main", 3, sizeof(vk_op_topk_moe_push_constants), {1, 1, 1}, {device->subgroup_size, 1u<num_additional_fused_ops) { + uint32_t idx = (uint32_t)ceilf(log2f(float(dst->ne[0]))); + GGML_ASSERT(idx < num_topk_moe_pipelines); +- bool with_norm = ctx->num_additional_fused_ops == topk_moe_norm.size() - 1; +- return ctx->device->pipeline_topk_moe[idx][with_norm]; ++ topk_moe_mode mode = ggml_vk_num_additional_ops_to_topk_moe_mode(ctx->num_additional_fused_ops); ++ return ctx->device->pipeline_topk_moe[idx][mode]; + } + + if (src0->type == GGML_TYPE_F32 && (src1 == nullptr || src1->type == GGML_TYPE_F32) && dst->type == GGML_TYPE_F32) { +@@ -8141,6 +8207,13 @@ static vk_pipeline ggml_vk_op_get_pipeline(ggml_backend_vk_context * ctx, const + return nullptr; + } + case GGML_OP_ARGSORT: ++ if (ctx->num_additional_fused_ops) { ++ uint32_t idx = (uint32_t)ceilf(log2f(float(dst->ne[0]))); ++ GGML_ASSERT(idx < num_topk_moe_pipelines); ++ topk_moe_mode mode = ggml_vk_num_additional_ops_to_topk_moe_mode(ctx->num_additional_fused_ops); ++ return ctx->device->pipeline_topk_moe[idx][mode]; ++ } ++ + if (src0->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_I32) { + uint32_t idx = (uint32_t)ceilf(log2f(float(dst->ne[0]))); + return ctx->device->pipeline_argsort_f32[idx]; +@@ -9676,10 +9749,12 @@ static void ggml_vk_soft_max_back(ggml_backend_vk_context * ctx, vk_context& sub + + static void ggml_vk_topk_moe(ggml_backend_vk_context * ctx, vk_context& subctx, ggml_cgraph * cgraph, int node_idx, bool dryrun = false) { + +- bool with_norm = ctx->num_additional_fused_ops == topk_moe_norm.size() - 1; ++ topk_moe_mode mode = ggml_vk_num_additional_ops_to_topk_moe_mode(ctx->num_additional_fused_ops); + ggml_tensor * logits = cgraph->nodes[node_idx + 0]->src[0]; +- ggml_tensor * weights = with_norm ? cgraph->nodes[node_idx + 8] : cgraph->nodes[node_idx + 4]; +- ggml_tensor * ids = cgraph->nodes[node_idx + 3]; ++ ggml_tensor * weights = (mode == TOPK_MOE_EARLY_SOFTMAX_NORM) ? cgraph->nodes[node_idx + 9] : ++ (mode == TOPK_MOE_EARLY_SOFTMAX) ? cgraph->nodes[node_idx + 4] : ++ cgraph->nodes[node_idx + 5]; ++ ggml_tensor * ids = (mode == TOPK_MOE_LATE_SOFTMAX) ? cgraph->nodes[node_idx + 1] : cgraph->nodes[node_idx + 3]; + + GGML_ASSERT(logits->type == GGML_TYPE_F32); + GGML_ASSERT(weights->type == GGML_TYPE_F32); +@@ -9738,9 +9813,14 @@ static void ggml_vk_topk_moe(ggml_backend_vk_context * ctx, vk_context& subctx, + GGML_ASSERT(d_ids != nullptr); + } + +- vk_op_topk_moe_push_constants pc; ++ vk_op_topk_moe_push_constants pc {}; + pc.n_rows = n_rows; + pc.n_expert_used = n_expert_used; ++ if (mode == TOPK_MOE_EARLY_SOFTMAX_NORM) { ++ ggml_tensor * clamp = cgraph->nodes[node_idx + 7]; ++ pc.clamp_min = ggml_get_op_params_f32(clamp, 0); ++ pc.clamp_max = ggml_get_op_params_f32(clamp, 1); ++ } + + GGML_ASSERT(n_expert_used <= n_experts); + +@@ -11335,7 +11415,13 @@ static bool ggml_vk_build_graph(ggml_backend_vk_context * ctx, ggml_cgraph * cgr + } + } + } ++ ++#define ENABLE_SYNC_LOGGING 0 ++ + if (need_sync) { ++#if ENABLE_SYNC_LOGGING ++ std::cerr << "sync" << std::endl; ++#endif + ctx->unsynced_nodes_written.clear(); + ctx->unsynced_nodes_read.clear(); + ggml_vk_sync_buffers(ctx, compute_ctx); +@@ -11353,6 +11439,18 @@ static bool ggml_vk_build_graph(ggml_backend_vk_context * ctx, ggml_cgraph * cgr + } + } + } ++#if ENABLE_SYNC_LOGGING ++ if (!dryrun) { ++ for (int i = 0; i < ctx->num_additional_fused_ops + 1; ++i) { ++ auto *n = cgraph->nodes[node_idx + i]; ++ std::cerr << node_idx + i << " " << ggml_op_name(n->op) << " " << n->name; ++ if (n->op == GGML_OP_GLU) { ++ std::cerr << " " << ggml_glu_op_name(ggml_get_glu_op(n)) << " " << (n->src[1] ? "split" : "single") << " "; ++ } ++ std::cerr << std::endl; ++ } ++ } ++#endif + + switch (node->op) { + case GGML_OP_REPEAT: +@@ -11531,7 +11629,11 @@ static bool ggml_vk_build_graph(ggml_backend_vk_context * ctx, ggml_cgraph * cgr + + break; + case GGML_OP_ARGSORT: +- ggml_vk_argsort(ctx, compute_ctx, src0, node, dryrun); ++ if (ctx->num_additional_fused_ops) { ++ ggml_vk_topk_moe(ctx, compute_ctx, cgraph, node_idx, dryrun); ++ } else { ++ ggml_vk_argsort(ctx, compute_ctx, src0, node, dryrun); ++ } + + break; + case GGML_OP_SUM: +@@ -12329,30 +12431,27 @@ static bool ggml_vk_can_fuse(const struct ggml_cgraph * cgraph, int node_idx, st + } + + static bool ggml_vk_can_fuse_topk_moe(ggml_backend_vk_context * ctx, const struct ggml_cgraph * cgraph, +- int node_idx, bool with_norm) { ++ int node_idx, topk_moe_mode mode) { + +- if (with_norm) { +- if (node_idx + (int)topk_moe_norm.size() > cgraph->n_nodes) { +- return false; +- } +- for (size_t i = 0; i < topk_moe_norm.size(); ++i) { +- if (cgraph->nodes[node_idx + i]->op != topk_moe_norm[i]) { +- return false; +- } +- } +- } else { +- if (node_idx + (int)topk_moe.size() > cgraph->n_nodes) { +- return false; +- } +- for (size_t i = 0; i < topk_moe.size(); ++i) { +- if (cgraph->nodes[node_idx + i]->op != topk_moe[i]) { +- return false; +- } +- } +- } ++ const ggml_tensor * softmax; ++ const ggml_tensor * weights; + +- const ggml_tensor * softmax = cgraph->nodes[node_idx + 0]; +- const ggml_tensor * weights = with_norm ? cgraph->nodes[node_idx + 8] : cgraph->nodes[node_idx + 4]; ++ switch (mode) { ++ case TOPK_MOE_EARLY_SOFTMAX_NORM: ++ softmax = cgraph->nodes[node_idx + 0]; ++ weights = cgraph->nodes[node_idx + 9]; ++ break; ++ case TOPK_MOE_EARLY_SOFTMAX: ++ softmax = cgraph->nodes[node_idx + 0]; ++ weights = cgraph->nodes[node_idx + 4]; ++ break; ++ case TOPK_MOE_LATE_SOFTMAX: ++ softmax = cgraph->nodes[node_idx + 4]; ++ weights = cgraph->nodes[node_idx + 5]; ++ break; ++ default: ++ return false; ++ } + + const float * op_params = (const float *)softmax->op_params; + +@@ -12378,60 +12477,6 @@ static bool ggml_vk_can_fuse_topk_moe(ggml_backend_vk_context * ctx, const struc + return false; + } + +- // Check that the nodes don't have any unexpected uses +- const ggml_tensor * reshape1 = cgraph->nodes[node_idx + 1]; +- const ggml_tensor * argsort = cgraph->nodes[node_idx + 2]; +- const ggml_tensor * view = cgraph->nodes[node_idx + 3]; +- const ggml_tensor * get_rows = cgraph->nodes[node_idx + 4]; +- const ggml_tensor * reshape5 = with_norm ? cgraph->nodes[node_idx + 5] : nullptr; +- const ggml_tensor * sum_rows = with_norm ? cgraph->nodes[node_idx + 6] : nullptr; +- const ggml_tensor * div = with_norm ? cgraph->nodes[node_idx + 7] : nullptr; +- const ggml_tensor * reshape8 = with_norm ? cgraph->nodes[node_idx + 8] : nullptr; +- +- // softmax is used by reshape and argsort +- if (ggml_node_get_use_count(cgraph, node_idx) != 2 || +- reshape1->src[0] != softmax || +- argsort->src[0] != softmax) { +- return false; +- } +- // reshape is used by get_rows +- if (ggml_node_get_use_count(cgraph, node_idx + 1) != 1 || +- get_rows->src[0] != reshape1) { +- return false; +- } +- // argsort is used by view +- if (ggml_node_get_use_count(cgraph, node_idx + 2) != 1 || +- view->src[0] != argsort) { +- return false; +- } +- // view is written (via argsort), we can skip checking it +- +- if (with_norm) { +- // get_rows is used by reshape +- if (ggml_node_get_use_count(cgraph, node_idx + 4) != 1 || +- reshape5->src[0] != get_rows) { +- return false; +- } +- +- // reshape is used by sum_rows and div +- if (ggml_node_get_use_count(cgraph, node_idx + 5) != 2 || +- sum_rows->src[0] != reshape5 || +- div->src[0] != reshape5) { +- return false; +- } +- +- // sum_rows is used by div +- if (ggml_node_get_use_count(cgraph, node_idx + 6) != 1 || +- div->src[1] != sum_rows) { +- return false; +- } +- +- // div/reshape are written +- if (reshape8->src[0] != div) { +- return false; +- } +- } +- + if (!ctx->device->subgroup_arithmetic || + !ctx->device->subgroup_shuffle || + !ctx->device->subgroup_require_full_support || +@@ -12517,10 +12562,18 @@ static ggml_status ggml_backend_vk_graph_compute(ggml_backend_t backend, ggml_cg + ctx->num_additional_fused_ops = num_adds - 1; + } else if (ggml_vk_can_fuse(cgraph, i, { GGML_OP_RMS_NORM, GGML_OP_MUL })) { + ctx->num_additional_fused_ops = 1; +- } else if (ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, true)) { +- ctx->num_additional_fused_ops = topk_moe_norm.size() - 1; +- } else if (ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, false)) { +- ctx->num_additional_fused_ops = topk_moe.size() - 1; ++ } else if (ggml_can_fuse_subgraph(cgraph, i, topk_moe_early_softmax_norm, { i + 3, i + 9 }) && ++ ggml_check_edges(cgraph, i, topk_moe_early_softmax_norm_edges) && ++ ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, TOPK_MOE_EARLY_SOFTMAX_NORM)) { ++ ctx->num_additional_fused_ops = topk_moe_early_softmax_norm.size() - 1; ++ } else if (ggml_can_fuse_subgraph(cgraph, i, topk_moe_early_softmax, { i + 3, i + 4 }) && ++ ggml_check_edges(cgraph, i, topk_moe_early_softmax_edges) && ++ ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, TOPK_MOE_EARLY_SOFTMAX)) { ++ ctx->num_additional_fused_ops = topk_moe_early_softmax.size() - 1; ++ } else if (ggml_can_fuse_subgraph(cgraph, i, topk_moe_late_softmax, { i + 1, i + 5 }) && ++ ggml_check_edges(cgraph, i, topk_moe_late_softmax_edges) && ++ ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, TOPK_MOE_LATE_SOFTMAX)) { ++ ctx->num_additional_fused_ops = topk_moe_late_softmax.size() - 1; + } + } + ggml_vk_build_graph(ctx, cgraph, i, nullptr, 0, true, false, false, false); +@@ -12618,10 +12671,18 @@ static ggml_status ggml_backend_vk_graph_compute(ggml_backend_t backend, ggml_cg + ctx->num_additional_fused_ops = num_adds - 1; + } else if (ggml_vk_can_fuse(cgraph, i, { GGML_OP_RMS_NORM, GGML_OP_MUL })) { + ctx->num_additional_fused_ops = 1; +- } else if (ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, true)) { +- ctx->num_additional_fused_ops = topk_moe_norm.size() - 1; +- } else if (ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, false)) { +- ctx->num_additional_fused_ops = topk_moe.size() - 1; ++ } else if (ggml_can_fuse_subgraph(cgraph, i, topk_moe_early_softmax_norm, { i + 3, i + 9 }) && ++ ggml_check_edges(cgraph, i, topk_moe_early_softmax_norm_edges) && ++ ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, TOPK_MOE_EARLY_SOFTMAX_NORM)) { ++ ctx->num_additional_fused_ops = topk_moe_early_softmax_norm.size() - 1; ++ } else if (ggml_can_fuse_subgraph(cgraph, i, topk_moe_early_softmax, { i + 3, i + 4 }) && ++ ggml_check_edges(cgraph, i, topk_moe_early_softmax_edges) && ++ ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, TOPK_MOE_EARLY_SOFTMAX)) { ++ ctx->num_additional_fused_ops = topk_moe_early_softmax.size() - 1; ++ } else if (ggml_can_fuse_subgraph(cgraph, i, topk_moe_late_softmax, { i + 1, i + 5 }) && ++ ggml_check_edges(cgraph, i, topk_moe_late_softmax_edges) && ++ ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, TOPK_MOE_LATE_SOFTMAX)) { ++ ctx->num_additional_fused_ops = topk_moe_late_softmax.size() - 1; + } + } + +@@ -12754,25 +12815,44 @@ static void ggml_vk_graph_optimize(ggml_backend_t backend, struct ggml_cgraph * + while (first_unused < graph->n_nodes) { + std::vector current_set; + +- // Avoid reordering topk_moe_norm +- if (first_unused + (int)topk_moe_norm.size() <= graph->n_nodes) { +- bool is_topk_moe_norm = true; +- for (size_t j = 0; j < topk_moe_norm.size(); ++j) { +- if (graph->nodes[first_unused + j]->op != topk_moe_norm[j] || used[first_unused + j]) { +- is_topk_moe_norm = false; ++ // Check for fusion patterns and avoid reordering them ++ auto const &match_pattern = [&](const std::initializer_list &pattern, int start) -> bool { ++ if (start + (int)pattern.size() <= graph->n_nodes) { ++ bool is_pattern = true; ++ for (size_t j = 0; j < pattern.size(); ++j) { ++ if (graph->nodes[start + j]->op != pattern.begin()[j] || used[start + j]) { ++ is_pattern = false; ++ } + } ++ return is_pattern; + } +- if (is_topk_moe_norm) { +- for (size_t j = 0; j < topk_moe_norm.size(); ++j) { ++ return false; ++ }; ++ ++ auto const &keep_pattern = [&](const std::initializer_list &pattern) -> bool { ++ if (match_pattern(pattern, first_unused)) { ++ for (size_t j = 0; j < pattern.size(); ++j) { + new_order.push_back(graph->nodes[first_unused + j]); + used[first_unused + j] = true; + } + while (first_unused < graph->n_nodes && used[first_unused]) { + first_unused++; + } +- continue; ++ return true; + } ++ return false; ++ }; ++ ++ if (keep_pattern(topk_moe_early_softmax_norm)) { ++ continue; ++ } ++ if (keep_pattern(topk_moe_early_softmax)) { ++ continue; + } ++ if (keep_pattern(topk_moe_late_softmax)) { ++ continue; ++ } ++ + // First, grab the next unused node. + current_set.push_back(first_unused); + +@@ -12790,6 +12870,12 @@ static void ggml_vk_graph_optimize(ggml_backend_t backend, struct ggml_cgraph * + if (is_empty(graph->nodes[j])) { + continue; + } ++ // Don't pull forward nodes from fusion patterns ++ if (match_pattern(topk_moe_early_softmax_norm, j) || ++ match_pattern(topk_moe_early_softmax, j) || ++ match_pattern(topk_moe_late_softmax, j)) { ++ continue; ++ } + bool ok = true; + for (int c = first_unused; c < j; ++c) { + if (!used[c] && +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/topk_moe.comp b/ggml/src/ggml-vulkan/vulkan-shaders/topk_moe.comp +index 9e56d5f8a..bc1c278bf 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/topk_moe.comp ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/topk_moe.comp +@@ -11,6 +11,8 @@ layout (push_constant) uniform parameter + { + uint n_rows; + uint n_expert_used; ++ float clamp_min; ++ float clamp_max; + }; + + layout(local_size_x_id = 0, local_size_y = 4, local_size_z = 1) in; +@@ -18,6 +20,7 @@ layout(local_size_x_id = 0, local_size_y = 4, local_size_z = 1) in; + layout(constant_id = 0) const uint WARP_SIZE = 32; + layout(constant_id = 1) const uint n_experts = 512; + layout(constant_id = 2) const bool with_norm = true; ++layout(constant_id = 3) const bool late_softmax = false; + + const uint experts_per_thread = (n_experts > WARP_SIZE) ? n_experts / WARP_SIZE : 1; + +@@ -25,53 +28,72 @@ layout (binding = 0, std430) readonly buffer Logits {float logits[];}; + layout (binding = 1, std430) writeonly buffer Weights {float weights[];}; + layout (binding = 2, std430) writeonly buffer Ids {uint ids[];}; + +-void main() { +- const uint row = gl_WorkGroupID.x * gl_WorkGroupSize.y + gl_LocalInvocationID.y; +- if (row >= n_rows) { +- return; +- } ++const float INFINITY = 1.0 / 0.0; + +- const uint logits_offset = n_experts * row; +- const uint weights_offset = n_expert_used * row; +- const uint ids_offset = n_experts * row; +- +- float logits_r[experts_per_thread]; +- +- const float INFINITY = 1.0 / 0.0; ++// Warp-local softmax used for both the pre-top-k logits and the post-top-k delayed path. ++void softmax_warp_inplace(inout float vals[experts_per_thread], const uint limit, const uint lane, const bool use_limit) { ++ float max_val = -INFINITY; + + [[unroll]] +- for (uint i = 0; i < n_experts; i += WARP_SIZE) { +- const uint expert = i + gl_LocalInvocationID.x; +- logits_r[i / WARP_SIZE] = n_experts % WARP_SIZE == 0 || expert < n_experts ? logits[logits_offset + expert] : -INFINITY; ++ for (int i = 0; i < experts_per_thread; i++) { ++ const uint idx = lane + i * WARP_SIZE; ++ const bool is_active = !use_limit || (idx < limit); ++ if (is_active) { ++ max_val = max(max_val, vals[i]); ++ } + } + +- float max_val = logits_r[0]; ++ max_val = subgroupMax(max_val); ++ ++ float sum = 0.f; + + [[unroll]] +- for (int i = 1; i < experts_per_thread; i++) { +- const float val = logits_r[i]; +- max_val = max(val, max_val); ++ for (int i = 0; i < experts_per_thread; i++) { ++ const uint idx = lane + i * WARP_SIZE; ++ const bool is_active = !use_limit || (idx < limit); ++ if (is_active) { ++ const float val = exp(vals[i] - max_val); ++ vals[i] = val; ++ sum += val; ++ } else { ++ vals[i] = 0.f; ++ } + } + +- max_val = subgroupMax(max_val); ++ sum = subgroupAdd(sum); + +- float wt[experts_per_thread]; +- float tmp = 0.f; ++ const float inv_sum = 1.0f / sum; + + [[unroll]] + for (int i = 0; i < experts_per_thread; i++) { +- const float val = logits_r[i]; +- wt[i] = exp(val - max_val); +- tmp += wt[i]; ++ const uint idx = lane + i * WARP_SIZE; ++ const bool is_active = !use_limit || (idx < limit); ++ if (is_active) { ++ vals[i] *= inv_sum; ++ } + } ++} + +- tmp = subgroupAdd(tmp); ++void main() { ++ const uint row = gl_WorkGroupID.x * gl_WorkGroupSize.y + gl_LocalInvocationID.y; ++ if (row >= n_rows) { ++ return; ++ } + +- const float inv_sum = 1.0f / tmp; ++ const uint logits_offset = n_experts * row; ++ const uint weights_offset = n_expert_used * row; ++ const uint ids_offset = n_experts * row; ++ ++ float wt[experts_per_thread]; + + [[unroll]] +- for (int i = 0; i < experts_per_thread; i++) { +- wt[i] = wt[i] * inv_sum; ++ for (uint i = 0; i < n_experts; i += WARP_SIZE) { ++ const uint expert = i + gl_LocalInvocationID.x; ++ wt[i / WARP_SIZE] = (n_experts % WARP_SIZE == 0 || expert < n_experts) ? logits[logits_offset + expert] : -INFINITY; ++ } ++ ++ if (!late_softmax) { ++ softmax_warp_inplace(wt, n_experts, gl_LocalInvocationID.x, false); + } + + // at this point, each thread holds a portion of softmax, +@@ -82,6 +104,11 @@ void main() { + + float output_weights[experts_per_thread]; + ++ [[unroll]] ++ for (int i = 0; i < experts_per_thread; i++) { ++ output_weights[i] = 0.f; ++ } ++ + for (int k = 0; k < n_expert_used; k++) { + float max_val = wt[0]; + uint max_expert = gl_LocalInvocationID.x; +@@ -121,6 +148,7 @@ void main() { + + if (with_norm) { + wt_sum = subgroupAdd(wt_sum); ++ wt_sum = clamp(wt_sum, clamp_min, clamp_max); + const float inv_sum = 1.0f / wt_sum; + + [[unroll]] +@@ -129,6 +157,10 @@ void main() { + } + } + ++ if (late_softmax) { ++ softmax_warp_inplace(output_weights, n_expert_used, gl_LocalInvocationID.x, true); ++ } ++ + [[unroll]] + for (uint i = 0; i < experts_per_thread; ++i) { + uint idx = i * WARP_SIZE + gl_LocalInvocationID.x; diff --git a/llama/patches/0032-vulkan-Fuse-rope-set_rows-16769.patch b/llama/patches/0032-vulkan-Fuse-rope-set_rows-16769.patch new file mode 100644 index 00000000..64c7ffa4 --- /dev/null +++ b/llama/patches/0032-vulkan-Fuse-rope-set_rows-16769.patch @@ -0,0 +1,1242 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jeff Bolz +Date: Wed, 29 Oct 2025 15:13:10 -0500 +Subject: [PATCH] vulkan: Fuse rope+set_rows (#16769) + +This pattern appears in a lot of models, the rope operation is applied right +before storing into the KV cache (usually on the K tensor). + +Add a path to some of the rope shaders that computes the destination address +based on the set_rows tensor. Compile variants of the shader with D_TYPE of +f16 (the usual KV cache type). + +Add a src3 operand to ggml_vk_op_f32 - sometimes rope uses three srcs and needs +the fourth for the row indices. + +Add fused_ops_write_mask to indicate which intermediate tensors need to write +their results to memory. Skipping writing the roped K value helps to allow more +nodes to run concurrently. + +Add logic to ggml_vk_graph_optimize to make ROPE+VIEW+SET_ROWS consecutive. It +rarely starts out that way in the graph. + +Add new backend tests. +--- + ggml/src/ggml-vulkan/ggml-vulkan.cpp | 334 +++++++++++++----- + .../ggml-vulkan/vulkan-shaders/rope_head.glsl | 2 + + .../ggml-vulkan/vulkan-shaders/rope_neox.comp | 13 +- + .../ggml-vulkan/vulkan-shaders/rope_norm.comp | 13 +- + .../vulkan-shaders/vulkan-shaders-gen.cpp | 4 + + tests/test-backend-ops.cpp | 122 +++++-- + 6 files changed, 371 insertions(+), 117 deletions(-) + +diff --git a/ggml/src/ggml-vulkan/ggml-vulkan.cpp b/ggml/src/ggml-vulkan/ggml-vulkan.cpp +index b2855b078..aaf4334b5 100644 +--- a/ggml/src/ggml-vulkan/ggml-vulkan.cpp ++++ b/ggml/src/ggml-vulkan/ggml-vulkan.cpp +@@ -458,6 +458,11 @@ static topk_moe_mode ggml_vk_num_additional_ops_to_topk_moe_mode(uint32_t num) { + return mode; + } + ++static constexpr std::initializer_list> rope_view_set_rows_edges { ++ { 1, 0, 0 }, // view->src[0] == rope ++ { 2, 0, 1 }, // set_rows->src[0] == view ++}; ++ + struct vk_device_struct { + std::recursive_mutex mutex; + +@@ -640,8 +645,8 @@ struct vk_device_struct { + vk_pipeline pipeline_soft_max_f32, pipeline_soft_max_f32_f16; + vk_pipeline pipeline_soft_max_f32_wg512, pipeline_soft_max_f32_f16_wg512; + vk_pipeline pipeline_soft_max_back_f32; +- vk_pipeline pipeline_rope_norm_f32, pipeline_rope_norm_f16; +- vk_pipeline pipeline_rope_neox_f32, pipeline_rope_neox_f16; ++ vk_pipeline pipeline_rope_norm_f32, pipeline_rope_norm_f16, pipeline_rope_norm_f32_f16; ++ vk_pipeline pipeline_rope_neox_f32, pipeline_rope_neox_f16, pipeline_rope_neox_f32_f16; + vk_pipeline pipeline_rope_multi_f32, pipeline_rope_multi_f16; + vk_pipeline pipeline_rope_vision_f32, pipeline_rope_vision_f16; + vk_pipeline pipeline_argsort_f32[num_argsort_pipelines]; +@@ -1054,6 +1059,7 @@ struct vk_op_rope_push_constants { + uint32_t s2; + int32_t sections[4]; + uint32_t is_back; ++ uint32_t set_rows_stride; + }; + + struct vk_op_soft_max_push_constants { +@@ -1563,6 +1569,10 @@ struct ggml_backend_vk_context { + // number of additional consecutive nodes that are being fused with the + // node currently being processed + int num_additional_fused_ops {}; ++ // Bitmask of which fused ops need to write an intermediate value to memory. ++ // Bit 'i' means nodes[start_of_fusion + i] writes to memory. ++ // If there's no fusion, bit 0 is still set. ++ int fused_ops_write_mask {}; + }; + + static void * const vk_ptr_base = (void *)(uintptr_t) 0x1000; // NOLINT +@@ -3697,21 +3707,27 @@ static void ggml_vk_load_shaders(vk_device& device) { + ggml_vk_create_pipeline(device, device->pipeline_soft_max_f32_f16_wg512, "soft_max_f32_f16_wg512", soft_max_f32_f16_len, soft_max_f32_f16_data, "main", 4, sizeof(vk_op_soft_max_push_constants), {1, 1, 1}, { 512 }, 1); + ggml_vk_create_pipeline(device, device->pipeline_soft_max_back_f32, "soft_max_back_f32", soft_max_back_f32_len, soft_max_back_f32_data, "main", 3, sizeof(vk_op_push_constants), {1, 1, 1}, { device->subgroup_size }, 1, true); + +- ggml_vk_create_pipeline(device, device->pipeline_rope_norm_f32, "rope_norm_f32", rope_norm_f32_len, rope_norm_f32_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); +- ggml_vk_create_pipeline(device, device->pipeline_rope_neox_f32, "rope_neox_f32", rope_neox_f32_len, rope_neox_f32_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); +- ggml_vk_create_pipeline(device, device->pipeline_rope_multi_f32, "rope_multi_f32", rope_multi_f32_len, rope_multi_f32_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); +- ggml_vk_create_pipeline(device, device->pipeline_rope_vision_f32, "rope_vision_f32", rope_vision_f32_len, rope_vision_f32_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); ++ ggml_vk_create_pipeline(device, device->pipeline_rope_norm_f32, "rope_norm_f32", rope_norm_f32_len, rope_norm_f32_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); ++ ggml_vk_create_pipeline(device, device->pipeline_rope_neox_f32, "rope_neox_f32", rope_neox_f32_len, rope_neox_f32_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); ++ ggml_vk_create_pipeline(device, device->pipeline_rope_multi_f32, "rope_multi_f32", rope_multi_f32_len, rope_multi_f32_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); ++ ggml_vk_create_pipeline(device, device->pipeline_rope_vision_f32, "rope_vision_f32", rope_vision_f32_len, rope_vision_f32_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); + + if (device->float_controls_rte_fp16) { +- ggml_vk_create_pipeline(device, device->pipeline_rope_norm_f16, "rope_norm_f16", rope_norm_f16_rte_len, rope_norm_f16_rte_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); +- ggml_vk_create_pipeline(device, device->pipeline_rope_neox_f16, "rope_neox_f16", rope_neox_f16_rte_len, rope_neox_f16_rte_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); +- ggml_vk_create_pipeline(device, device->pipeline_rope_multi_f16, "rope_multi_f16", rope_multi_f16_rte_len, rope_multi_f16_rte_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); +- ggml_vk_create_pipeline(device, device->pipeline_rope_vision_f16, "rope_vision_f16", rope_vision_f16_rte_len, rope_vision_f16_rte_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); ++ ggml_vk_create_pipeline(device, device->pipeline_rope_norm_f16, "rope_norm_f16", rope_norm_f16_rte_len, rope_norm_f16_rte_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); ++ ggml_vk_create_pipeline(device, device->pipeline_rope_neox_f16, "rope_neox_f16", rope_neox_f16_rte_len, rope_neox_f16_rte_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); ++ ggml_vk_create_pipeline(device, device->pipeline_rope_multi_f16, "rope_multi_f16", rope_multi_f16_rte_len, rope_multi_f16_rte_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); ++ ggml_vk_create_pipeline(device, device->pipeline_rope_vision_f16, "rope_vision_f16", rope_vision_f16_rte_len, rope_vision_f16_rte_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); ++ ++ ggml_vk_create_pipeline(device, device->pipeline_rope_norm_f32_f16, "rope_norm_f32_f16", rope_norm_f32_f16_rte_len, rope_norm_f32_f16_rte_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); ++ ggml_vk_create_pipeline(device, device->pipeline_rope_neox_f32_f16, "rope_neox_f32_f16", rope_neox_f32_f16_rte_len, rope_neox_f32_f16_rte_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); + } else { +- ggml_vk_create_pipeline(device, device->pipeline_rope_norm_f16, "rope_norm_f16", rope_norm_f16_len, rope_norm_f16_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); +- ggml_vk_create_pipeline(device, device->pipeline_rope_neox_f16, "rope_neox_f16", rope_neox_f16_len, rope_neox_f16_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); +- ggml_vk_create_pipeline(device, device->pipeline_rope_multi_f16, "rope_multi_f16", rope_multi_f16_len, rope_multi_f16_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); +- ggml_vk_create_pipeline(device, device->pipeline_rope_vision_f16, "rope_vision_f16", rope_vision_f16_len, rope_vision_f16_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); ++ ggml_vk_create_pipeline(device, device->pipeline_rope_norm_f16, "rope_norm_f16", rope_norm_f16_len, rope_norm_f16_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); ++ ggml_vk_create_pipeline(device, device->pipeline_rope_neox_f16, "rope_neox_f16", rope_neox_f16_len, rope_neox_f16_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); ++ ggml_vk_create_pipeline(device, device->pipeline_rope_multi_f16, "rope_multi_f16", rope_multi_f16_len, rope_multi_f16_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); ++ ggml_vk_create_pipeline(device, device->pipeline_rope_vision_f16, "rope_vision_f16", rope_vision_f16_len, rope_vision_f16_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); ++ ++ ggml_vk_create_pipeline(device, device->pipeline_rope_norm_f32_f16, "rope_norm_f32_f16", rope_norm_f32_f16_len, rope_norm_f32_f16_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); ++ ggml_vk_create_pipeline(device, device->pipeline_rope_neox_f32_f16, "rope_neox_f32_f16", rope_neox_f32_f16_len, rope_neox_f32_f16_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); + } + + for (uint32_t i = 0; i < num_argsort_pipelines; ++i) { +@@ -8170,7 +8186,8 @@ static vk_pipeline ggml_vk_op_get_pipeline(ggml_backend_vk_context * ctx, const + case GGML_OP_ROPE: + case GGML_OP_ROPE_BACK: + { +- const int mode = ((const int32_t *) dst->op_params)[2]; ++ const ggml_tensor *rope = ctx->num_additional_fused_ops == 2 ? dst->src[0]->src[0] : dst; ++ const int mode = ((const int32_t *) rope->op_params)[2]; + const bool is_neox = mode & GGML_ROPE_TYPE_NEOX; + const bool is_mrope = mode & GGML_ROPE_TYPE_MROPE; + const bool is_vision = mode == GGML_ROPE_TYPE_VISION; +@@ -8179,6 +8196,9 @@ static vk_pipeline ggml_vk_op_get_pipeline(ggml_backend_vk_context * ctx, const + if (src0->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32) { + return ctx->device->pipeline_rope_neox_f32; + } ++ if (src0->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F16) { ++ return ctx->device->pipeline_rope_neox_f32_f16; ++ } + if (src0->type == GGML_TYPE_F16 && dst->type == GGML_TYPE_F16) { + return ctx->device->pipeline_rope_neox_f16; + } +@@ -8200,6 +8220,9 @@ static vk_pipeline ggml_vk_op_get_pipeline(ggml_backend_vk_context * ctx, const + if (src0->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32) { + return ctx->device->pipeline_rope_norm_f32; + } ++ if (src0->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F16) { ++ return ctx->device->pipeline_rope_norm_f32_f16; ++ } + if (src0->type == GGML_TYPE_F16 && dst->type == GGML_TYPE_F16) { + return ctx->device->pipeline_rope_norm_f16; + } +@@ -8409,20 +8432,22 @@ static uint32_t get_misalign_bytes(ggml_backend_vk_context * ctx, const ggml_ten + return ((vk_tensor_offset(t) + t->view_offs) & (ctx->device->properties.limits.minStorageBufferOffsetAlignment - 1));; + } + +-template void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, T &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst) { ++template void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, T &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, const ggml_tensor * src3, ggml_tensor * dst) { + GGML_UNUSED(p); + GGML_UNUSED(src0); + GGML_UNUSED(src1); + GGML_UNUSED(src2); ++ GGML_UNUSED(src3); + GGML_UNUSED(dst); + static_assert(!std::is_const::value, "unexpected type"); + GGML_ASSERT(!src0 || get_misalign_bytes(ctx, src0) == 0); + GGML_ASSERT(!src1 || get_misalign_bytes(ctx, src1) == 0); + GGML_ASSERT(!src2 || get_misalign_bytes(ctx, src2) == 0); ++ GGML_ASSERT(!src3 || get_misalign_bytes(ctx, src3) == 0); + GGML_ASSERT(!dst || get_misalign_bytes(ctx, dst) == 0); + } + +-template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_unary_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst) { ++template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_unary_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, const ggml_tensor * src3, ggml_tensor * dst) { + const uint32_t a_offset = get_misalign_bytes(ctx, src0) / ggml_type_size(src0->type); + const uint32_t d_offset = get_misalign_bytes(ctx, dst) / ggml_type_size(dst->type); + +@@ -8430,9 +8455,10 @@ template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk + + GGML_UNUSED(src1); + GGML_UNUSED(src2); ++ GGML_UNUSED(src3); + } + +-template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_sum_rows_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst) { ++template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_sum_rows_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, const ggml_tensor * src3, ggml_tensor * dst) { + const uint32_t a_offset = get_misalign_bytes(ctx, src0) / ggml_type_size(src0->type); + const uint32_t d_offset = get_misalign_bytes(ctx, dst) / ggml_type_size(dst->type); + +@@ -8440,9 +8466,10 @@ template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk + + GGML_UNUSED(src1); + GGML_UNUSED(src2); ++ GGML_UNUSED(src3); + } + +-template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_pad_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst) { ++template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_pad_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, const ggml_tensor * src3, ggml_tensor * dst) { + const uint32_t a_offset = get_misalign_bytes(ctx, src0) / ggml_type_size(src0->type); + const uint32_t d_offset = get_misalign_bytes(ctx, dst) / ggml_type_size(dst->type); + +@@ -8450,9 +8477,10 @@ template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk + + GGML_UNUSED(src1); + GGML_UNUSED(src2); ++ GGML_UNUSED(src3); + } + +-template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_im2col_3d_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst) { ++template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_im2col_3d_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, const ggml_tensor * src3, ggml_tensor * dst) { + const uint32_t a_offset = get_misalign_bytes(ctx, src1) / ggml_type_size(src1->type); + const uint32_t d_offset = get_misalign_bytes(ctx, dst) / ggml_type_size(dst->type); + +@@ -8460,9 +8488,10 @@ template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk + + GGML_UNUSED(src0); + GGML_UNUSED(src2); ++ GGML_UNUSED(src3); + } + +-template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_binary_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst) { ++template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_binary_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, const ggml_tensor * src3, ggml_tensor * dst) { + const uint32_t a_offset = get_misalign_bytes(ctx, src0) / ggml_type_size(src0->type); + const uint32_t b_offset = get_misalign_bytes(ctx, src1) / ggml_type_size(src1->type); + const uint32_t d_offset = get_misalign_bytes(ctx, dst) / ggml_type_size(dst->type); +@@ -8472,9 +8501,10 @@ template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk + p.misalign_offsets = (a_offset << 16) | (b_offset << 8) | d_offset; + + GGML_UNUSED(src2); ++ GGML_UNUSED(src3); + } + +-template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_upscale_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst) { ++template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_upscale_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, const ggml_tensor * src3, ggml_tensor * dst) { + const uint32_t a_offset = get_misalign_bytes(ctx, src0) / ggml_type_size(src0->type); + const uint32_t d_offset = get_misalign_bytes(ctx, dst) / ggml_type_size(dst->type); + +@@ -8483,10 +8513,11 @@ template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk + + GGML_UNUSED(src1); + GGML_UNUSED(src2); ++ GGML_UNUSED(src3); + } + + template +-static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst, ggml_op op, PC&& pc, bool dryrun = false) { ++static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, const ggml_tensor * src3, ggml_tensor * dst, ggml_op op, PC&& pc, bool dryrun = false) { + VK_LOG_DEBUG("ggml_vk_op_f32((" << src0 << ", name=" << src0->name << ", type=" << src0->type << ", ne0=" << src0->ne[0] << ", ne1=" << src0->ne[1] << ", ne2=" << src0->ne[2] << ", ne3=" << src0->ne[3] << ", nb0=" << src0->nb[0] << ", nb1=" << src0->nb[1] << ", nb2=" << src0->nb[2] << ", nb3=" << src0->nb[3]; + if (src1 != nullptr) { + std::cerr << "), (" << src1 << ", name=" << src1->name << ", type=" << src1->type << ", ne0=" << src1->ne[0] << ", ne1=" << src1->ne[1] << ", ne2=" << src1->ne[2] << ", ne3=" << src1->ne[3] << ", nb0=" << src1->nb[0] << ", nb1=" << src1->nb[1] << ", nb2=" << src1->nb[2] << ", nb3=" << src1->nb[3]; +@@ -8494,6 +8525,9 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co + if (src2 != nullptr) { + std::cerr << "), (" << src2 << ", name=" << src2->name << ", type=" << src2->type << ", ne0=" << src2->ne[0] << ", ne1=" << src2->ne[1] << ", ne2=" << src2->ne[2] << ", ne3=" << src2->ne[3] << ", nb0=" << src2->nb[0] << ", nb1=" << src2->nb[1] << ", nb2=" << src2->nb[2] << ", nb3=" << src2->nb[3]; + } ++ if (src3 != nullptr) { ++ std::cerr << "), (" << src3 << ", name=" << src3->name << ", type=" << src3->type << ", ne0=" << src3->ne[0] << ", ne1=" << src3->ne[1] << ", ne2=" << src3->ne[2] << ", ne3=" << src3->ne[3] << ", nb0=" << src3->nb[0] << ", nb1=" << src3->nb[1] << ", nb2=" << src3->nb[2] << ", nb3=" << src3->nb[3]; ++ } + std::cerr << "), (" << dst << ", name=" << dst->name << ", type=" << dst->type << ", ne0=" << dst->ne[0] << ", ne1=" << dst->ne[1] << ", ne2=" << dst->ne[2] << ", ne3=" << dst->ne[3] << ", nb0=" << dst->nb[0] << ", nb1=" << dst->nb[1] << ", nb2=" << dst->nb[2] << ", nb3=" << dst->nb[3]; + std::cerr << "), " << ggml_op_name(op) << ", " << (dryrun ? "dryrun" : "") << ")"); + GGML_ASSERT(op == GGML_OP_GET_ROWS || op == GGML_OP_CPY || (!ggml_is_quantized(src0->type) && (src1 == nullptr || !ggml_is_quantized(src1->type)))); // NOLINT +@@ -8520,6 +8554,13 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co + const uint64_t ne23 = use_src2 ? src2->ne[3] : 0; + const uint64_t ne2 = ne20 * ne21; + ++ const bool use_src3 = src3 != nullptr; ++ const uint64_t ne30 = use_src3 ? src3->ne[0] : 0; ++ const uint64_t ne31 = use_src3 ? src3->ne[1] : 0; ++ const uint64_t ne32 = use_src3 ? src3->ne[2] : 0; ++ const uint64_t ne33 = use_src3 ? src3->ne[3] : 0; ++ const uint64_t ne3 = ne30 * ne31; ++ + const uint64_t ned0 = dst->ne[0]; + const uint64_t ned1 = dst->ne[1]; + const uint64_t ned2 = dst->ne[2]; +@@ -8550,6 +8591,7 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co + ggml_backend_vk_buffer_context * src0_buf_ctx = (ggml_backend_vk_buffer_context *)src0->buffer->context; + ggml_backend_vk_buffer_context * src1_buf_ctx = use_src1 ? (ggml_backend_vk_buffer_context *)src1->buffer->context : nullptr; + ggml_backend_vk_buffer_context * src2_buf_ctx = use_src2 ? (ggml_backend_vk_buffer_context *)src2->buffer->context : nullptr; ++ ggml_backend_vk_buffer_context * src3_buf_ctx = use_src3 ? (ggml_backend_vk_buffer_context *)src3->buffer->context : nullptr; + + vk_buffer d_X = nullptr; + size_t x_buf_offset = 0; +@@ -8557,10 +8599,13 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co + size_t y_buf_offset = 0; + vk_buffer d_Z = nullptr; + size_t z_buf_offset = 0; ++ vk_buffer d_W = nullptr; ++ size_t w_buf_offset = 0; + + bool src0_uma = false; + bool src1_uma = false; + bool src2_uma = false; ++ bool src3_uma = false; + + if (ctx->device->uma) { + ggml_vk_host_get(ctx->device, src0->data, d_X, x_buf_offset); +@@ -8573,6 +8618,10 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co + ggml_vk_host_get(ctx->device, src2->data, d_Z, z_buf_offset); + src2_uma = d_Z != nullptr; + } ++ if (use_src3) { ++ ggml_vk_host_get(ctx->device, src3->data, d_W, w_buf_offset); ++ src3_uma = d_W != nullptr; ++ } + } + + vk_buffer d_D = dst_buf_ctx->dev_buffer; +@@ -8594,11 +8643,17 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co + z_buf_offset = vk_tensor_offset(src2) + src2->view_offs; + GGML_ASSERT(d_Z != nullptr); + } ++ if (use_src3 && !src3_uma) { ++ d_W = src3_buf_ctx->dev_buffer; ++ w_buf_offset = vk_tensor_offset(src3) + src3->view_offs; ++ GGML_ASSERT(d_W != nullptr); ++ } + // Compute misalignment offset for descriptors and store it in in push constants, then align the descriptor offsets. +- init_pushconst_tensor_offsets(ctx, pc, src0, src1, src2, dst); ++ init_pushconst_tensor_offsets(ctx, pc, src0, src1, src2, src3, dst); + x_buf_offset &= ~(ctx->device->properties.limits.minStorageBufferOffsetAlignment - 1); + y_buf_offset &= ~(ctx->device->properties.limits.minStorageBufferOffsetAlignment - 1); + z_buf_offset &= ~(ctx->device->properties.limits.minStorageBufferOffsetAlignment - 1); ++ w_buf_offset &= ~(ctx->device->properties.limits.minStorageBufferOffsetAlignment - 1); + d_buf_offset &= ~(ctx->device->properties.limits.minStorageBufferOffsetAlignment - 1); + + std::array elements; +@@ -8799,12 +8854,13 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co + break; + } + +- uint64_t x_sz, y_sz, z_sz, d_sz; ++ uint64_t x_sz, y_sz, z_sz, w_sz, d_sz; + + if (op_supports_incontiguous) { + x_sz = ggml_nbytes(src0) + get_misalign_bytes(ctx, src0); + y_sz = use_src1 ? ggml_nbytes(src1) + get_misalign_bytes(ctx, src1) : 0; + z_sz = use_src2 ? ggml_nbytes(src2) + get_misalign_bytes(ctx, src2) : 0; ++ w_sz = use_src3 ? ggml_nbytes(src3) + get_misalign_bytes(ctx, src3) : 0; + d_sz = ggml_nbytes(dst) + get_misalign_bytes(ctx, dst); + + if (x_buf_offset + x_sz >= d_X->size) { +@@ -8816,6 +8872,9 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co + if (use_src2 && z_buf_offset + z_sz >= d_Z->size) { + z_sz = ggml_vk_get_max_buffer_range(ctx, d_Z, z_buf_offset); + } ++ if (use_src3 && w_buf_offset + w_sz >= d_W->size) { ++ w_sz = ggml_vk_get_max_buffer_range(ctx, d_W, w_buf_offset); ++ } + if (d_buf_offset + d_sz >= d_D->size) { + d_sz = ggml_vk_get_max_buffer_range(ctx, d_D, d_buf_offset); + } +@@ -8823,6 +8882,7 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co + x_sz = ggml_type_size(src0->type)/ggml_blck_size(src0->type) * ne0 * ne02 * ne03; + y_sz = use_src1 ? ggml_type_size(src1->type) * ne1 * ne12 * ne13 : 0; + z_sz = use_src2 ? ggml_type_size(src2->type) * ne2 * ne22 * ne23 : 0; ++ w_sz = use_src3 ? ggml_type_size(src3->type) * ne3 * ne32 * ne33 : 0; + d_sz = ggml_type_size(dst->type) * ned * ned2 * ned3; + } + +@@ -8864,14 +8924,19 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co + ggml_vk_dispatch_pipeline(ctx, subctx, pipeline, { vk_subbuffer{ d_X, x_buf_offset, x_sz }, subbuf_y, subbuf_z, vk_subbuffer{ d_D, d_buf_offset, d_sz } }, pc, elements); + } else if (op == GGML_OP_ROPE || op == GGML_OP_ROPE_BACK) { + // Empty src2 is possible in rope, but the shader needs a buffer +- vk_subbuffer subbuf_z; ++ vk_subbuffer subbuf_z, subbuf_w; + if (use_src2) { + subbuf_z = { d_Z, z_buf_offset, z_sz }; + } else { + subbuf_z = { d_X, 0, x_sz }; + } ++ if (use_src3) { ++ subbuf_w = { d_W, w_buf_offset, w_sz }; ++ } else { ++ subbuf_w = { d_X, 0, x_sz }; ++ } + +- ggml_vk_dispatch_pipeline(ctx, subctx, pipeline, { vk_subbuffer{ d_X, x_buf_offset, x_sz }, vk_subbuffer{ d_Y, y_buf_offset, y_sz }, subbuf_z, vk_subbuffer{ d_D, d_buf_offset, d_sz } }, pc, elements); ++ ggml_vk_dispatch_pipeline(ctx, subctx, pipeline, { vk_subbuffer{ d_X, x_buf_offset, x_sz }, vk_subbuffer{ d_Y, y_buf_offset, y_sz }, subbuf_z, vk_subbuffer{ d_D, d_buf_offset, d_sz }, subbuf_w }, pc, elements); + } else if (op == GGML_OP_IM2COL || op == GGML_OP_IM2COL_3D) { + if (ctx->device->shader_int64 && ctx->device->buffer_device_address) { + // buffer device address path doesn't use dst buffer +@@ -8887,6 +8952,8 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co + } else if (op == GGML_OP_OPT_STEP_SGD) { + // OPT_STEP_SGD works on src0, it does not need dst + ggml_vk_dispatch_pipeline(ctx, subctx, pipeline, { vk_subbuffer{ d_X, x_buf_offset, x_sz }, vk_subbuffer{ d_Y, y_buf_offset, y_sz }, vk_subbuffer{ d_Z, z_buf_offset, z_sz } }, pc, elements); ++ } else if (use_src3) { ++ ggml_vk_dispatch_pipeline(ctx, subctx, pipeline, { vk_subbuffer{ d_X, x_buf_offset, x_sz }, vk_subbuffer{ d_Y, y_buf_offset, y_sz }, vk_subbuffer{ d_Z, z_buf_offset, z_sz }, vk_subbuffer{ d_W, w_buf_offset, w_sz }, vk_subbuffer{ d_D, d_buf_offset, d_sz } }, pc, elements); + } else if (use_src2) { + ggml_vk_dispatch_pipeline(ctx, subctx, pipeline, { vk_subbuffer{ d_X, x_buf_offset, x_sz }, vk_subbuffer{ d_Y, y_buf_offset, y_sz }, vk_subbuffer{ d_Z, z_buf_offset, z_sz }, vk_subbuffer{ d_D, d_buf_offset, d_sz } }, pc, elements); + } else if (use_src1) { +@@ -8901,7 +8968,7 @@ static void ggml_vk_get_rows(ggml_backend_vk_context * ctx, vk_context& subctx, + const uint32_t src1_type_size = ggml_type_size(src1->type); + const uint32_t dst_type_size = ggml_type_size(dst->type); + +- ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_GET_ROWS, { ++ ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_GET_ROWS, { + (uint32_t)ggml_nelements(src0), + (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2],(uint32_t)src0->ne[3], (uint32_t)src0->nb[0] / src0_type_size, (uint32_t)src0->nb[1] / src0_type_size, (uint32_t)src0->nb[2] / src0_type_size, (uint32_t)src0->nb[3] / src0_type_size, + (uint32_t)src1->ne[0], (uint32_t)src1->ne[1], (uint32_t)src1->ne[2],(uint32_t)src1->ne[3], (uint32_t)src1->nb[0] / src1_type_size, (uint32_t)src1->nb[1] / src1_type_size, (uint32_t)src1->nb[2] / src1_type_size, (uint32_t)src1->nb[3] / src1_type_size, +@@ -8921,7 +8988,7 @@ static void ggml_vk_acc(ggml_backend_vk_context * ctx, vk_context& subctx, const + // int nb3 = dst->op_params[2] / 4; // 4 bytes of float32 - unused + int offset = dst->op_params[3] / 4; // offset in bytes + +- ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_ACC, { ++ ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_ACC, { + (uint32_t)ggml_nelements(src0), + (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2],(uint32_t)src0->ne[3], (uint32_t)src0->nb[0] / src0_type_size, (uint32_t)nb1, (uint32_t)nb2, (uint32_t)src0->nb[3] / src0_type_size, + (uint32_t)src1->ne[0], (uint32_t)src1->ne[1], (uint32_t)src1->ne[2],(uint32_t)src1->ne[3], (uint32_t)src1->nb[0] / src1_type_size, (uint32_t)src1->nb[1] / src1_type_size, (uint32_t)src1->nb[2] / src1_type_size, (uint32_t)src1->nb[3] / src1_type_size, +@@ -9046,7 +9113,7 @@ static void ggml_vk_add(ggml_backend_vk_context * ctx, vk_context& subctx, const + const uint32_t src1_type_size = ggml_type_size(src1->type); + const uint32_t dst_type_size = ggml_type_size(dst->type); + +- ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_ADD, { ++ ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_ADD, { + (uint32_t)ggml_nelements(src0), + (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2],(uint32_t)src0->ne[3], (uint32_t)src0->nb[0] / src0_type_size, (uint32_t)src0->nb[1] / src0_type_size, (uint32_t)src0->nb[2] / src0_type_size, (uint32_t)src0->nb[3] / src0_type_size, + (uint32_t)src1->ne[0], (uint32_t)src1->ne[1], (uint32_t)src1->ne[2],(uint32_t)src1->ne[3], (uint32_t)src1->nb[0] / src1_type_size, (uint32_t)src1->nb[1] / src1_type_size, (uint32_t)src1->nb[2] / src1_type_size, (uint32_t)src1->nb[3] / src1_type_size, +@@ -9061,7 +9128,7 @@ static void ggml_vk_sub(ggml_backend_vk_context * ctx, vk_context& subctx, const + const uint32_t src1_type_size = ggml_type_size(src1->type); + const uint32_t dst_type_size = ggml_type_size(dst->type); + +- ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_SUB, { ++ ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_SUB, { + (uint32_t)ggml_nelements(src0), + (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2],(uint32_t)src0->ne[3], (uint32_t)src0->nb[0] / src0_type_size, (uint32_t)src0->nb[1] / src0_type_size, (uint32_t)src0->nb[2] / src0_type_size, (uint32_t)src0->nb[3] / src0_type_size, + (uint32_t)src1->ne[0], (uint32_t)src1->ne[1], (uint32_t)src1->ne[2],(uint32_t)src1->ne[3], (uint32_t)src1->nb[0] / src1_type_size, (uint32_t)src1->nb[1] / src1_type_size, (uint32_t)src1->nb[2] / src1_type_size, (uint32_t)src1->nb[3] / src1_type_size, +@@ -9076,7 +9143,7 @@ static void ggml_vk_mul(ggml_backend_vk_context * ctx, vk_context& subctx, const + const uint32_t src1_type_size = ggml_type_size(src1->type); + const uint32_t dst_type_size = ggml_type_size(dst->type); + +- ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_MUL, { ++ ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_MUL, { + (uint32_t)ggml_nelements(src0), + (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2],(uint32_t)src0->ne[3], (uint32_t)src0->nb[0] / src0_type_size, (uint32_t)src0->nb[1] / src0_type_size, (uint32_t)src0->nb[2] / src0_type_size, (uint32_t)src0->nb[3] / src0_type_size, + (uint32_t)src1->ne[0], (uint32_t)src1->ne[1], (uint32_t)src1->ne[2],(uint32_t)src1->ne[3], (uint32_t)src1->nb[0] / src1_type_size, (uint32_t)src1->nb[1] / src1_type_size, (uint32_t)src1->nb[2] / src1_type_size, (uint32_t)src1->nb[3] / src1_type_size, +@@ -9091,7 +9158,7 @@ static void ggml_vk_div(ggml_backend_vk_context * ctx, vk_context& subctx, const + const uint32_t src1_type_size = ggml_type_size(src1->type); + const uint32_t dst_type_size = ggml_type_size(dst->type); + +- ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_DIV, { ++ ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_DIV, { + (uint32_t)ggml_nelements(src0), + (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2],(uint32_t)src0->ne[3], (uint32_t)src0->nb[0] / src0_type_size, (uint32_t)src0->nb[1] / src0_type_size, (uint32_t)src0->nb[2] / src0_type_size, (uint32_t)src0->nb[3] / src0_type_size, + (uint32_t)src1->ne[0], (uint32_t)src1->ne[1], (uint32_t)src1->ne[2],(uint32_t)src1->ne[3], (uint32_t)src1->nb[0] / src1_type_size, (uint32_t)src1->nb[1] / src1_type_size, (uint32_t)src1->nb[2] / src1_type_size, (uint32_t)src1->nb[3] / src1_type_size, +@@ -9106,7 +9173,7 @@ static void ggml_vk_add_id(ggml_backend_vk_context * ctx, vk_context& subctx, co + const uint32_t src1_type_size = ggml_type_size(src1->type); + const uint32_t src2_type_size = ggml_type_size(src2->type); + +- ggml_vk_op_f32(ctx, subctx, src0, src1, src2, dst, GGML_OP_ADD_ID, { ++ ggml_vk_op_f32(ctx, subctx, src0, src1, src2, nullptr, dst, GGML_OP_ADD_ID, { + (uint32_t)dst->ne[0], + (uint32_t)dst->ne[1], + (uint32_t)src0->nb[1] / src0_type_size, +@@ -9339,7 +9406,7 @@ static void ggml_vk_ssm_conv(ggml_backend_vk_context * ctx, vk_context& subctx, + const ggml_tensor * src0 = dst->src[0]; + const ggml_tensor * src1 = dst->src[1]; + +- ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_SSM_CONV, { ++ ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_SSM_CONV, { + (uint32_t)src0->nb[1], (uint32_t)src0->nb[2], + (uint32_t)src1->nb[1], + (uint32_t)dst->nb[0], (uint32_t)dst->nb[1], (uint32_t)dst->nb[2], +@@ -9457,7 +9524,7 @@ static void ggml_vk_opt_step_adamw(ggml_backend_vk_context * ctx, vk_context& su + static void ggml_vk_opt_step_sgd(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst, bool dryrun = false) { + const size_t n = ggml_nelements(dst->src[0]); + +- ggml_vk_op_f32(ctx, subctx, src0, src1, src2, dst, GGML_OP_OPT_STEP_SGD, { (uint32_t)n, 0, 0.0f, 0.0f }, dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, src1, src2, nullptr, dst, GGML_OP_OPT_STEP_SGD, { (uint32_t)n, 0, 0.0f, 0.0f }, dryrun); + } + + static void ggml_vk_concat(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, bool dryrun = false) { +@@ -9467,7 +9534,7 @@ static void ggml_vk_concat(ggml_backend_vk_context * ctx, vk_context& subctx, co + const uint32_t src1_type_size = ggml_type_size(src1->type); + const uint32_t dst_type_size = ggml_type_size(dst->type); + +- ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_CONCAT, { ++ ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_CONCAT, { + (uint32_t)ggml_nelements(dst), + (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2],(uint32_t)src0->ne[3], (uint32_t)src0->nb[0] / src0_type_size, (uint32_t)src0->nb[1] / src0_type_size, (uint32_t)src0->nb[2] / src0_type_size, (uint32_t)src0->nb[3] / src0_type_size, + (uint32_t)src1->ne[0], (uint32_t)src1->ne[1], (uint32_t)src1->ne[2],(uint32_t)src1->ne[3], (uint32_t)src1->nb[0] / src1_type_size, (uint32_t)src1->nb[1] / src1_type_size, (uint32_t)src1->nb[2] / src1_type_size, (uint32_t)src1->nb[3] / src1_type_size, +@@ -9491,7 +9558,7 @@ static void ggml_vk_upscale(ggml_backend_vk_context * ctx, vk_context& subctx, c + sf1 = (float)(dst->ne[1] - 1) / (src0->ne[1] - 1); + } + +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_UPSCALE, { ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_UPSCALE, { + (uint32_t)ggml_nelements(dst), 0, 0, + (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], + (uint32_t)src0->nb[0] / src0_type_size, (uint32_t)src0->nb[1] / src0_type_size, (uint32_t)src0->nb[2] / src0_type_size, (uint32_t)src0->nb[3] / src0_type_size, +@@ -9505,23 +9572,23 @@ static void ggml_vk_scale(ggml_backend_vk_context * ctx, vk_context& subctx, con + p.param1 = ggml_get_op_params_f32(dst, 0); + p.param2 = ggml_get_op_params_f32(dst, 1); + +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_SCALE, std::move(p), dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_SCALE, std::move(p), dryrun); + } + + static void ggml_vk_sqr(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_SQR, vk_op_unary_push_constants_init(src0, dst), dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_SQR, vk_op_unary_push_constants_init(src0, dst), dryrun); + } + + static void ggml_vk_sqrt(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_SQRT, vk_op_unary_push_constants_init(src0, dst), dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_SQRT, vk_op_unary_push_constants_init(src0, dst), dryrun); + } + + static void ggml_vk_sin(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_SIN, vk_op_unary_push_constants_init(src0, dst), dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_SIN, vk_op_unary_push_constants_init(src0, dst), dryrun); + } + + static void ggml_vk_cos(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_COS, vk_op_unary_push_constants_init(src0, dst), dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_COS, vk_op_unary_push_constants_init(src0, dst), dryrun); + } + + static void ggml_vk_clamp(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { +@@ -9529,12 +9596,12 @@ static void ggml_vk_clamp(ggml_backend_vk_context * ctx, vk_context& subctx, con + p.param1 = ggml_get_op_params_f32(dst, 0); + p.param2 = ggml_get_op_params_f32(dst, 1); + +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_CLAMP, std::move(p), dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_CLAMP, std::move(p), dryrun); + } + + static void ggml_vk_pad(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { + vk_op_pad_push_constants p = vk_op_pad_push_constants_init(src0, dst); +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_PAD, std::move(p), dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_PAD, std::move(p), dryrun); + } + + static void ggml_vk_roll(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { +@@ -9549,17 +9616,17 @@ static void ggml_vk_roll(ggml_backend_vk_context * ctx, vk_context& subctx, cons + memcpy(&p.param1, &s01_packed, sizeof(float)); + memcpy(&p.param2, &s23_packed, sizeof(float)); + +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_ROLL, std::move(p), dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_ROLL, std::move(p), dryrun); + } + + static void ggml_vk_repeat(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { + vk_op_unary_push_constants p = vk_op_unary_push_constants_init(src0, dst, ggml_nelements(dst)); +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_REPEAT, std::move(p), dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_REPEAT, std::move(p), dryrun); + } + + static void ggml_vk_repeat_back(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { + vk_op_unary_push_constants p = vk_op_unary_push_constants_init(src0, dst, ggml_nelements(dst)); +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_REPEAT_BACK, std::move(p), dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_REPEAT_BACK, std::move(p), dryrun); + } + + static void ggml_vk_cpy(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { +@@ -9575,7 +9642,7 @@ static void ggml_vk_cpy(ggml_backend_vk_context * ctx, vk_context& subctx, const + } + + vk_op_unary_push_constants p = vk_op_unary_push_constants_init(src0, dst, ne); +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_CPY, std::move(p), dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_CPY, std::move(p), dryrun); + } + + static void ggml_vk_set_rows(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, bool dryrun = false) { +@@ -9590,7 +9657,7 @@ static void ggml_vk_set_rows(ggml_backend_vk_context * ctx, vk_context& subctx, + return; + } + +- ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_SET_ROWS, { ++ ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_SET_ROWS, { + (uint32_t)ggml_nelements(src0), + (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2],(uint32_t)src0->ne[3], (uint32_t)src0->nb[0] / src0_type_size, (uint32_t)src0->nb[1] / src0_type_size, (uint32_t)src0->nb[2] / src0_type_size, (uint32_t)src0->nb[3] / src0_type_size, + (uint32_t)src1->ne[0], (uint32_t)src1->ne[1], (uint32_t)src1->ne[2],(uint32_t)src1->ne[3], (uint32_t)src1->nb[0] / src1_type_size, (uint32_t)src1->nb[1] / src1_type_size, (uint32_t)src1->nb[2] / src1_type_size, (uint32_t)src1->nb[3] / src1_type_size, +@@ -9601,13 +9668,13 @@ static void ggml_vk_set_rows(ggml_backend_vk_context * ctx, vk_context& subctx, + } + + static void ggml_vk_silu_back(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, bool dryrun = false) { +- ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_SILU_BACK, { (uint32_t)ggml_nelements(src0), 0, 0.0f, 0.0f }, dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_SILU_BACK, { (uint32_t)ggml_nelements(src0), 0, 0.0f, 0.0f }, dryrun); + } + + static void ggml_vk_norm(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { + float * op_params = (float *)dst->op_params; + +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_NORM, { (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], op_params[0], 0.0f }, dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_NORM, { (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], op_params[0], 0.0f }, dryrun); + } + + static void ggml_vk_group_norm(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { +@@ -9618,7 +9685,7 @@ static void ggml_vk_group_norm(ggml_backend_vk_context * ctx, vk_context& subctx + const float eps = float_op_params[1]; + const uint32_t group_size = src0->ne[0] * src0->ne[1] * ((src0->ne[2] + num_groups - 1) / num_groups); + +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_GROUP_NORM, { group_size, 0, eps, 0.0f }, dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_GROUP_NORM, { group_size, 0, eps, 0.0f }, dryrun); + } + + static uint32_t ggml_vk_rms_num_partials(ggml_backend_vk_context * ctx, const ggml_tensor *node) { +@@ -9641,7 +9708,7 @@ static void ggml_vk_rms_norm(ggml_backend_vk_context * ctx, vk_context& subctx, + + uint32_t param3 = ctx->do_add_rms_partials ? ggml_vk_rms_num_partials(ctx, dst) : 0; + +- ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_RMS_NORM, { ++ ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_RMS_NORM, { + (uint32_t)ggml_nelements(src0), + (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2],(uint32_t)src0->ne[3], (uint32_t)src0->nb[0] / src0_type_size, (uint32_t)src0->nb[1] / src0_type_size, (uint32_t)src0->nb[2] / src0_type_size, (uint32_t)src0->nb[3] / src0_type_size, + (uint32_t)src1->ne[0], (uint32_t)src1->ne[1], (uint32_t)src1->ne[2],(uint32_t)src1->ne[3], (uint32_t)src1->nb[0] / src1_type_size, (uint32_t)src1->nb[1] / src1_type_size, (uint32_t)src1->nb[2] / src1_type_size, (uint32_t)src1->nb[3] / src1_type_size, +@@ -9658,16 +9725,16 @@ static void ggml_vk_rms_norm(ggml_backend_vk_context * ctx, vk_context& subctx, + + static void ggml_vk_rms_norm_back(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, bool dryrun = false) { + float * op_params = (float *)dst->op_params; +- ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_RMS_NORM_BACK, { (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], op_params[0], 0.0f }, dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_RMS_NORM_BACK, { (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], op_params[0], 0.0f }, dryrun); + } + + static void ggml_vk_l2_norm(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { + float * op_params = (float *)dst->op_params; +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_L2_NORM, { (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], op_params[0], 0.0f }, dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_L2_NORM, { (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], op_params[0], 0.0f }, dryrun); + } + + static void ggml_vk_unary(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_UNARY, { (uint32_t)ggml_nelements(src0), 0, 0.0f, 0.0f }, dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_UNARY, { (uint32_t)ggml_nelements(src0), 0, 0.0f, 0.0f }, dryrun); + } + + static void ggml_vk_glu(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, bool dryrun = false) { +@@ -9690,7 +9757,7 @@ static void ggml_vk_glu(ggml_backend_vk_context * ctx, vk_context& subctx, const + + const uint32_t mode = split ? 2 : (swapped ? 1 : 0); + +- ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_GLU, ++ ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_GLU, + { + (uint32_t)ggml_nelements(dst), + (uint32_t)src0->ne[0], +@@ -9703,7 +9770,7 @@ static void ggml_vk_glu(ggml_backend_vk_context * ctx, vk_context& subctx, const + + static void ggml_vk_diag_mask_inf(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { + int32_t * op_params = (int32_t *)dst->op_params; +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_DIAG_MASK_INF, { (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], op_params[0] }, dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_DIAG_MASK_INF, { (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], op_params[0] }, dryrun); + } + + static void ggml_vk_soft_max(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst, bool dryrun = false) { +@@ -9728,7 +9795,7 @@ static void ggml_vk_soft_max(ggml_backend_vk_context * ctx, vk_context& subctx, + const float m0 = powf(2.0f, -(max_bias ) / n_head_log2); + const float m1 = powf(2.0f, -(max_bias / 2.0f) / n_head_log2); + +- ggml_vk_op_f32(ctx, subctx, src0, src1, src2, dst, GGML_OP_SOFT_MAX, { ++ ggml_vk_op_f32(ctx, subctx, src0, src1, src2, nullptr, dst, GGML_OP_SOFT_MAX, { + ncols, + src1 != nullptr ? nrows_y : (uint32_t)0, + (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2], +@@ -9744,7 +9811,7 @@ static void ggml_vk_soft_max(ggml_backend_vk_context * ctx, vk_context& subctx, + + static void ggml_vk_soft_max_back(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, bool dryrun = false) { + float * op_params = (float *)dst->op_params; +- ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_SOFT_MAX_BACK, { (uint32_t)src0->ne[0], (uint32_t)ggml_nrows(src0), op_params[0], op_params[1] }, dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_SOFT_MAX_BACK, { (uint32_t)src0->ne[0], (uint32_t)ggml_nrows(src0), op_params[0], op_params[1] }, dryrun); + } + + static void ggml_vk_topk_moe(ggml_backend_vk_context * ctx, vk_context& subctx, ggml_cgraph * cgraph, int node_idx, bool dryrun = false) { +@@ -9835,7 +9902,12 @@ static void ggml_vk_topk_moe(ggml_backend_vk_context * ctx, vk_context& subctx, + }, pc, elements); + } + +-static void ggml_vk_rope(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst, bool backprop, bool dryrun = false) { ++static void ggml_vk_rope(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_cgraph * cgraph, int node_idx, bool backprop, bool dryrun = false) { ++ ggml_tensor * dst = cgraph->nodes[node_idx]; ++ const ggml_tensor * src0 = dst->src[0]; ++ const ggml_tensor * src1 = dst->src[1]; ++ const ggml_tensor * src2 = dst->src[2]; ++ const ggml_tensor * src3 = nullptr; + const int n_dims = ((int32_t *) dst->op_params)[1]; + const int mode = ((int32_t *) dst->op_params)[2]; + // const int n_ctx = ((int32_t *) dst->op_params)[3]; +@@ -9859,11 +9931,20 @@ static void ggml_vk_rope(ggml_backend_vk_context * ctx, vk_context& subctx, cons + uint32_t s1 = src0->nb[1] / ggml_type_size(src0->type); + uint32_t s2 = src0->nb[2] / ggml_type_size(src0->type); + +- ggml_vk_op_f32(ctx, subctx, src0, src1, src2, dst, GGML_OP_ROPE, { ++ uint32_t set_rows_stride = 0; ++ // Fused rope + view + set_rows passes the set_rows destination stride in set_rows_stride ++ // and overrides the dst and sets src3=row_indices ++ if (ctx->num_additional_fused_ops > 0) { ++ set_rows_stride = cgraph->nodes[node_idx + 2]->nb[1] / ggml_type_size(cgraph->nodes[node_idx + 2]->type); ++ src3 = cgraph->nodes[node_idx + 2]->src[1]; ++ dst = cgraph->nodes[node_idx + 2]; ++ } ++ ++ ggml_vk_op_f32(ctx, subctx, src0, src1, src2, src3, dst, GGML_OP_ROPE, { + (uint32_t)src0->ne[0], (uint32_t)n_dims, freq_scale, (uint32_t)src0->ne[1], + freq_base, ext_factor, attn_factor, {corr_dims[0], corr_dims[1]}, theta_scale, + src2 != nullptr, (uint32_t)src0->ne[2], s1, s2, +- { sections[0], sections[1], sections[2], sections[3] }, backprop ++ { sections[0], sections[1], sections[2], sections[3] }, backprop, set_rows_stride, + }, dryrun); + } + +@@ -9872,7 +9953,7 @@ static void ggml_vk_argsort(ggml_backend_vk_context * ctx, vk_context& subctx, c + + uint32_t ncols = src0->ne[0]; + +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_ARGSORT, { ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_ARGSORT, { + ncols, + op_params[0], + }, dryrun); +@@ -9880,26 +9961,26 @@ static void ggml_vk_argsort(ggml_backend_vk_context * ctx, vk_context& subctx, c + + static void ggml_vk_sum(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { + vk_op_sum_rows_push_constants p = vk_op_sum_rows_push_constants_init(src0, dst, ggml_nelements(src0)); +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_SUM, p, dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_SUM, p, dryrun); + } + + static void ggml_vk_sum_rows(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { + vk_op_sum_rows_push_constants p = vk_op_sum_rows_push_constants_init(src0, dst, src0->ne[0]); +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_SUM_ROWS, p, dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_SUM_ROWS, p, dryrun); + } + + static void ggml_vk_mean(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { + vk_op_sum_rows_push_constants p = vk_op_sum_rows_push_constants_init(src0, dst, src0->ne[0]); + p.weight = 1.0f / (float)src0->ne[0]; +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_MEAN, p, dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_MEAN, p, dryrun); + } + + static void ggml_vk_argmax(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_ARGMAX, { (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], 0.0f, 0.0f }, dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_ARGMAX, { (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], 0.0f, 0.0f }, dryrun); + } + + static void ggml_vk_count_equal(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, bool dryrun = false) { +- ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_COUNT_EQUAL, { (uint32_t)ggml_nelements(src0), 0, 0.0f, 0.0f }, dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_COUNT_EQUAL, { (uint32_t)ggml_nelements(src0), 0, 0.0f, 0.0f }, dryrun); + } + + static void ggml_vk_im2col(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, bool dryrun = false) { +@@ -9932,7 +10013,7 @@ static void ggml_vk_im2col(ggml_backend_vk_context * ctx, vk_context& subctx, co + + const vk::DeviceAddress dst_addr = d_buf->bda_addr + vk_tensor_offset(dst) + dst->view_offs; + +- ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_IM2COL, { ++ ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_IM2COL, { + dst_addr, + batch_offset, offset_delta, + IC, IW, IH, OW, OH, KW, KH, +@@ -10005,7 +10086,7 @@ static void ggml_vk_im2col_3d(ggml_backend_vk_context * ctx, vk_context& subctx, + pc.OH_OW_IC_KD_KH_KW = OH*OW*IC*KD*KH*KW; + pc.OW_IC_KD_KH_KW = OW*IC*KD*KH*KW; + +- ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_IM2COL_3D, std::move(pc), dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_IM2COL_3D, std::move(pc), dryrun); + } + + static void ggml_vk_timestep_embedding(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { +@@ -10013,7 +10094,7 @@ static void ggml_vk_timestep_embedding(ggml_backend_vk_context * ctx, vk_context + const uint32_t max_period = dst->op_params[1]; + const uint32_t nb1 = dst->nb[1] / ggml_type_size(dst->type); + +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_TIMESTEP_EMBEDDING, { ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_TIMESTEP_EMBEDDING, { + nb1, dim, max_period, + }, dryrun); + } +@@ -10046,7 +10127,7 @@ static void ggml_vk_conv_transpose_1d(ggml_backend_vk_context * ctx, vk_context& + p.nb1 = static_cast(nb1 / nb0); + p.s0 = static_cast(s0); + +- ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_CONV_TRANSPOSE_1D, std::move(p), dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_CONV_TRANSPOSE_1D, std::move(p), dryrun); + } + + static void ggml_vk_pool_2d(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { +@@ -10069,7 +10150,7 @@ static void ggml_vk_pool_2d(ggml_backend_vk_context * ctx, vk_context& subctx, c + + const uint32_t parallel_elements = N * OC * OH * OW; + +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_POOL_2D, { ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_POOL_2D, { + IW, IH, OW, OH, OC, + parallel_elements, + op, +@@ -10123,7 +10204,7 @@ static void ggml_vk_conv_2d(ggml_backend_vk_context * ctx, vk_context & subctx, + GGML_ASSERT(ne03 == ne2); + GGML_ASSERT(ne02 == ne12); + +- ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_CONV_2D, std::move(p), dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_CONV_2D, std::move(p), dryrun); + } + + static void ggml_vk_conv_transpose_2d(ggml_backend_vk_context * ctx, vk_context & subctx, const ggml_tensor * src0, +@@ -10172,7 +10253,7 @@ static void ggml_vk_conv_transpose_2d(ggml_backend_vk_context * ctx, vk_context + GGML_ASSERT(ne02 == ne2); + GGML_ASSERT(ne03 == ne12); + +- ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_CONV_TRANSPOSE_2D, std::move(p), dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_CONV_TRANSPOSE_2D, std::move(p), dryrun); + } + + static void ggml_vk_conv_2d_dw(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, bool dryrun = false) { +@@ -10196,12 +10277,12 @@ static void ggml_vk_conv_2d_dw(ggml_backend_vk_context * ctx, vk_context& subctx + GGML_ASSERT(src0->ne[3] == p.channels); + GGML_ASSERT(src1->ne[3] == p.batches); + +- ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_CONV_2D_DW, std::move(p), dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_CONV_2D_DW, std::move(p), dryrun); + } + + static void ggml_vk_leaky_relu(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { + const float * op_params = (const float *)dst->op_params; +- ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_LEAKY_RELU, { (uint32_t)ggml_nelements(src0), 0, op_params[0], 0.0f }, dryrun); ++ ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_LEAKY_RELU, { (uint32_t)ggml_nelements(src0), 0, op_params[0], 0.0f }, dryrun); + } + + #ifdef GGML_VULKAN_RUN_TESTS +@@ -11327,7 +11408,6 @@ static bool ggml_vk_build_graph(ggml_backend_vk_context * ctx, ggml_cgraph * cgr + case GGML_OP_DIAG_MASK_INF: + case GGML_OP_SOFT_MAX: + case GGML_OP_SOFT_MAX_BACK: +- case GGML_OP_ROPE: + case GGML_OP_ROPE_BACK: + case GGML_OP_ARGSORT: + case GGML_OP_SUM: +@@ -11401,9 +11481,12 @@ static bool ggml_vk_build_graph(ggml_backend_vk_context * ctx, ggml_cgraph * cgr + // nodes require synchronization. + for (int32_t i = 0; i < ctx->num_additional_fused_ops + 1 && !need_sync; ++i) { + const ggml_tensor *cur_node = cgraph->nodes[node_idx + i]; +- if (overlaps_unsynced(cur_node, ctx->unsynced_nodes_read) || overlaps_unsynced(cur_node, ctx->unsynced_nodes_written)) { +- need_sync = true; +- break; ++ // If the node actually writes to memory, then check if it needs to sync ++ if (ctx->fused_ops_write_mask & (1 << i)) { ++ if (overlaps_unsynced(cur_node, ctx->unsynced_nodes_read) || overlaps_unsynced(cur_node, ctx->unsynced_nodes_written)) { ++ need_sync = true; ++ break; ++ } + } + for (uint32_t j = 0; j < GGML_MAX_SRC; ++j) { + if (!cur_node->src[j]) { +@@ -11430,7 +11513,9 @@ static bool ggml_vk_build_graph(ggml_backend_vk_context * ctx, ggml_cgraph * cgr + for (int32_t i = 0; i < ctx->num_additional_fused_ops + 1; ++i) { + const ggml_tensor *cur_node = cgraph->nodes[node_idx + i]; + // Multiple outputs could be written, e.g. in topk_moe. Add them all to the list. +- ctx->unsynced_nodes_written.push_back(cur_node); ++ if (ctx->fused_ops_write_mask & (1 << i)) { ++ ctx->unsynced_nodes_written.push_back(cur_node); ++ } + for (uint32_t j = 0; j < GGML_MAX_SRC; ++j) { + if (!cur_node->src[j]) { + continue; +@@ -11621,11 +11706,11 @@ static bool ggml_vk_build_graph(ggml_backend_vk_context * ctx, ggml_cgraph * cgr + + break; + case GGML_OP_ROPE: +- ggml_vk_rope(ctx, compute_ctx, src0, src1, src2, node, false, dryrun); ++ ggml_vk_rope(ctx, compute_ctx, cgraph, node_idx, false, dryrun); + + break; + case GGML_OP_ROPE_BACK: +- ggml_vk_rope(ctx, compute_ctx, src0, src1, src2, node, true, dryrun); ++ ggml_vk_rope(ctx, compute_ctx, cgraph, node_idx, true, dryrun); + + break; + case GGML_OP_ARGSORT: +@@ -12487,6 +12572,41 @@ static bool ggml_vk_can_fuse_topk_moe(ggml_backend_vk_context * ctx, const struc + return true; + } + ++static bool ggml_vk_can_fuse_rope_set_rows(ggml_backend_vk_context * ctx, const struct ggml_cgraph * cgraph, ++ int node_idx) { ++ GGML_UNUSED(ctx); ++ const ggml_tensor *rope = cgraph->nodes[node_idx + 0]; ++ const ggml_tensor *view = cgraph->nodes[node_idx + 1]; ++ const ggml_tensor *set_rows = cgraph->nodes[node_idx + 2]; ++ ++ // ne3 not tested ++ if (rope->src[0]->ne[3] != 1) { ++ return false; ++ } ++ ++ if (set_rows->type != GGML_TYPE_F32 && set_rows->type != GGML_TYPE_F16) { ++ return false; ++ } ++ ++ if (set_rows->src[1]->type != GGML_TYPE_I64) { ++ return false; ++ } ++ ++ // The view should flatten two dims of rope into one dim ++ if (!ggml_is_contiguous(view) || ++ view->ne[0] != rope->ne[0] * rope->ne[1]) { ++ return false; ++ } ++ ++ // Only norm/neox shaders have the fusion code ++ const int mode = ((const int32_t *) rope->op_params)[2]; ++ if (mode != GGML_ROPE_TYPE_NORMAL && mode != GGML_ROPE_TYPE_NEOX) { ++ return false; ++ } ++ ++ return true; ++} ++ + static uint32_t ggml_vk_fuse_multi_add(ggml_backend_vk_context * ctx, const struct ggml_cgraph * cgraph, int node_idx) { + + const ggml_tensor *first_node = cgraph->nodes[node_idx]; +@@ -12562,6 +12682,10 @@ static ggml_status ggml_backend_vk_graph_compute(ggml_backend_t backend, ggml_cg + ctx->num_additional_fused_ops = num_adds - 1; + } else if (ggml_vk_can_fuse(cgraph, i, { GGML_OP_RMS_NORM, GGML_OP_MUL })) { + ctx->num_additional_fused_ops = 1; ++ } else if (ggml_can_fuse_subgraph(cgraph, i, { GGML_OP_ROPE, GGML_OP_VIEW, GGML_OP_SET_ROWS }, { i + 2 }) && ++ ggml_check_edges(cgraph, i, rope_view_set_rows_edges) && ++ ggml_vk_can_fuse_rope_set_rows(ctx, cgraph, i)) { ++ ctx->num_additional_fused_ops = 2; + } else if (ggml_can_fuse_subgraph(cgraph, i, topk_moe_early_softmax_norm, { i + 3, i + 9 }) && + ggml_check_edges(cgraph, i, topk_moe_early_softmax_norm_edges) && + ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, TOPK_MOE_EARLY_SOFTMAX_NORM)) { +@@ -12671,20 +12795,31 @@ static ggml_status ggml_backend_vk_graph_compute(ggml_backend_t backend, ggml_cg + ctx->num_additional_fused_ops = num_adds - 1; + } else if (ggml_vk_can_fuse(cgraph, i, { GGML_OP_RMS_NORM, GGML_OP_MUL })) { + ctx->num_additional_fused_ops = 1; ++ } else if (ggml_can_fuse_subgraph(cgraph, i, { GGML_OP_ROPE, GGML_OP_VIEW, GGML_OP_SET_ROWS }, { i + 2 }) && ++ ggml_check_edges(cgraph, i, rope_view_set_rows_edges) && ++ ggml_vk_can_fuse_rope_set_rows(ctx, cgraph, i)) { ++ ctx->num_additional_fused_ops = 2; + } else if (ggml_can_fuse_subgraph(cgraph, i, topk_moe_early_softmax_norm, { i + 3, i + 9 }) && + ggml_check_edges(cgraph, i, topk_moe_early_softmax_norm_edges) && + ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, TOPK_MOE_EARLY_SOFTMAX_NORM)) { + ctx->num_additional_fused_ops = topk_moe_early_softmax_norm.size() - 1; ++ // view of argsort writes to memory ++ ctx->fused_ops_write_mask |= 1 << 3; + } else if (ggml_can_fuse_subgraph(cgraph, i, topk_moe_early_softmax, { i + 3, i + 4 }) && + ggml_check_edges(cgraph, i, topk_moe_early_softmax_edges) && + ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, TOPK_MOE_EARLY_SOFTMAX)) { + ctx->num_additional_fused_ops = topk_moe_early_softmax.size() - 1; ++ // view of argsort writes to memory ++ ctx->fused_ops_write_mask |= 1 << 3; + } else if (ggml_can_fuse_subgraph(cgraph, i, topk_moe_late_softmax, { i + 1, i + 5 }) && + ggml_check_edges(cgraph, i, topk_moe_late_softmax_edges) && + ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, TOPK_MOE_LATE_SOFTMAX)) { + ctx->num_additional_fused_ops = topk_moe_late_softmax.size() - 1; ++ // view of argsort writes to memory ++ ctx->fused_ops_write_mask |= 1 << 1; + } + } ++ ctx->fused_ops_write_mask |= 1 << ctx->num_additional_fused_ops; + + // Signal the almost_ready fence when the graph is mostly complete (< 20% remaining) + bool almost_ready = (cgraph->n_nodes - i) < cgraph->n_nodes / 5; +@@ -12730,6 +12865,7 @@ static ggml_status ggml_backend_vk_graph_compute(ggml_backend_t backend, ggml_cg + } + i += ctx->num_additional_fused_ops; + ctx->num_additional_fused_ops = 0; ++ ctx->fused_ops_write_mask = 0; + } + + if (vk_perf_logger_enabled) { +@@ -12887,6 +13023,32 @@ static void ggml_vk_graph_optimize(ggml_backend_t backend, struct ggml_cgraph * + } + if (ok) { + current_set.push_back(j); ++ // Look for ROPE + VIEW + SET_ROWS and make them consecutive ++ if (graph->nodes[j]->op == GGML_OP_ROPE) { ++ int view_idx = -1; ++ int set_rows_idx = -1; ++ for (int k = j+1; k < std::min(j + 10, graph->n_nodes); ++k) { ++ if (view_idx == -1 && ++ graph->nodes[k]->op == GGML_OP_VIEW && ++ graph->nodes[k]->src[0] == graph->nodes[j]) { ++ view_idx = k; ++ continue; ++ } ++ if (view_idx != -1 && ++ set_rows_idx == -1 && ++ graph->nodes[k]->op == GGML_OP_SET_ROWS && ++ graph->nodes[k]->src[0] == graph->nodes[view_idx]) { ++ set_rows_idx = k; ++ break; ++ } ++ } ++ if (set_rows_idx != -1) { ++ current_set.push_back(view_idx); ++ current_set.push_back(set_rows_idx); ++ used[view_idx] = true; ++ used[set_rows_idx] = true; ++ } ++ } + } + } + // Second pass grabs view nodes. +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/rope_head.glsl b/ggml/src/ggml-vulkan/vulkan-shaders/rope_head.glsl +index 50fc1f1e2..0eda186c8 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/rope_head.glsl ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/rope_head.glsl +@@ -10,6 +10,7 @@ layout (binding = 0) readonly buffer X {A_TYPE data_a[];}; + layout (binding = 1) readonly buffer Y {int data_pos[];}; + layout (binding = 2) readonly buffer Z {float data_ff[];}; + layout (binding = 3) writeonly buffer D {D_TYPE data_d[];}; ++layout (binding = 4) readonly buffer I {uvec2 data_i[];}; // indices for set_rows + + layout (push_constant) uniform parameter { + uint ncols; +@@ -27,6 +28,7 @@ layout (push_constant) uniform parameter { + uint s2; + int sections[4]; + uint is_back; ++ uint set_rows_stride; + } p; + + float rope_yarn_ramp(const float low, const float high, const uint i0) { +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/rope_neox.comp b/ggml/src/ggml-vulkan/vulkan-shaders/rope_neox.comp +index 06e095bef..9f4538155 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/rope_neox.comp ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/rope_neox.comp +@@ -16,12 +16,19 @@ void main() { + const uint row_x = row_dst % ne1; + const uint channel_x = row_dst / ne1; + +- const uint idst = row_dst*ne0 + i0/2; ++ uint idst = row_dst*ne0 + i0/2; + const uint ix = channel_x*p.s2 + row_x*p.s1 + i0/2; + ++ // Fusion optimization: ROPE + VIEW + SET_ROWS.. ++ // The rope output is viewed as a 1D tensor and offset based on a row index in data_i. ++ if (p.set_rows_stride != 0) { ++ idst = row_x*ne0 + i0/2; ++ idst += data_i[channel_x].x * p.set_rows_stride; ++ } ++ + if (i0 >= p.n_dims) { +- data_d[idst + i0/2 + 0] = data_a[ix + i0/2 + 0]; +- data_d[idst + i0/2 + 1] = data_a[ix + i0/2 + 1]; ++ data_d[idst + i0/2 + 0] = D_TYPE(data_a[ix + i0/2 + 0]); ++ data_d[idst + i0/2 + 1] = D_TYPE(data_a[ix + i0/2 + 1]); + + return; + } +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/rope_norm.comp b/ggml/src/ggml-vulkan/vulkan-shaders/rope_norm.comp +index 6ba957540..f4209ed95 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/rope_norm.comp ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/rope_norm.comp +@@ -16,12 +16,19 @@ void main() { + const uint row_x = row_dst % ne1; + const uint channel_x = row_dst / ne1; + +- const uint idst = row_dst*ne0 + i0; ++ uint idst = row_dst*ne0 + i0; + const uint ix = channel_x*p.s2 + row_x*p.s1 + i0; + ++ // Fusion optimization: ROPE + VIEW + SET_ROWS.. ++ // The rope output is viewed as a 1D tensor and offset based on a row index in data_i. ++ if (p.set_rows_stride != 0) { ++ idst = row_x*ne0 + i0; ++ idst += data_i[channel_x].x * p.set_rows_stride; ++ } ++ + if (i0 >= p.n_dims) { +- data_d[idst + 0] = data_a[ix + 0]; +- data_d[idst + 1] = data_a[ix + 1]; ++ data_d[idst + 0] = D_TYPE(data_a[ix + 0]); ++ data_d[idst + 1] = D_TYPE(data_a[ix + 1]); + + return; + } +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/vulkan-shaders-gen.cpp b/ggml/src/ggml-vulkan/vulkan-shaders/vulkan-shaders-gen.cpp +index 03fa01639..e6ec589fb 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/vulkan-shaders-gen.cpp ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/vulkan-shaders-gen.cpp +@@ -842,10 +842,14 @@ void process_shaders() { + string_to_spv("rope_norm_f32", "rope_norm.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float"}}); + string_to_spv("rope_norm_f16", "rope_norm.comp", {{"A_TYPE", "float16_t"}, {"D_TYPE", "float16_t"}}); + string_to_spv("rope_norm_f16_rte", "rope_norm.comp", {{"A_TYPE", "float16_t"}, {"D_TYPE", "float16_t"}, {"RTE16", "1"}}); ++ string_to_spv("rope_norm_f32_f16", "rope_norm.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float16_t"}}); ++ string_to_spv("rope_norm_f32_f16_rte", "rope_norm.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float16_t"}, {"RTE16", "1"}}); + + string_to_spv("rope_neox_f32", "rope_neox.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float"}}); + string_to_spv("rope_neox_f16", "rope_neox.comp", {{"A_TYPE", "float16_t"}, {"D_TYPE", "float16_t"}}); + string_to_spv("rope_neox_f16_rte", "rope_neox.comp", {{"A_TYPE", "float16_t"}, {"D_TYPE", "float16_t"}, {"RTE16", "1"}}); ++ string_to_spv("rope_neox_f32_f16", "rope_neox.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float16_t"}}); ++ string_to_spv("rope_neox_f32_f16_rte", "rope_neox.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float16_t"}, {"RTE16", "1"}}); + + string_to_spv("rope_multi_f32", "rope_multi.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float"}}); + string_to_spv("rope_multi_f16", "rope_multi.comp", {{"A_TYPE", "float16_t"}, {"D_TYPE", "float16_t"}}); +diff --git a/tests/test-backend-ops.cpp b/tests/test-backend-ops.cpp +index 9eb2b6687..657b6cc2f 100644 +--- a/tests/test-backend-ops.cpp ++++ b/tests/test-backend-ops.cpp +@@ -2105,6 +2105,34 @@ struct test_get_rows_back : public test_case { + } + }; + ++static void init_set_rows_row_ids(ggml_tensor * t, int num_rows) { ++ std::random_device rd; ++ std::default_random_engine rng(rd()); ++ for (int i2 = 0; i2 < t->ne[2]; i2++) { ++ for (int i1 = 0; i1 < t->ne[1]; i1++) { ++ // generate a shuffled subset of row indices ++ std::vector data(num_rows); ++ for (int i = 0; i < num_rows; i++) { ++ data[i] = i; ++ } ++ std::shuffle(data.begin(), data.end(), rng); ++ data.resize(t->ne[0]); ++ ++ const size_t offs = i1*t->nb[1] + i2*t->nb[2]; ++ if (t->type == GGML_TYPE_I32) { ++ // TODO: Make a template or something ++ std::vector data_i32(t->ne[0]); ++ for (int i = 0; i < t->ne[0]; i++) { ++ data_i32[i] = static_cast(data[i]); ++ } ++ ggml_backend_tensor_set(t, data_i32.data(), offs, t->ne[0]*sizeof(int32_t)); ++ } else { ++ ggml_backend_tensor_set(t, data.data(), offs, t->ne[0]*sizeof(int64_t)); ++ } ++ } ++ } ++} ++ + // GGML_OP_SET_ROWS + struct test_set_rows : public test_case { + const ggml_type type; +@@ -2148,37 +2176,13 @@ struct test_set_rows : public test_case { + } + + void initialize_tensors(ggml_context * ctx) override { +- std::random_device rd; +- std::default_random_engine rng(rd()); + for (ggml_tensor * t = ggml_get_first_tensor(ctx); t != NULL; t = ggml_get_next_tensor(ctx, t)) { + if (t->type == GGML_TYPE_I64 || t->type == GGML_TYPE_I32) { + if (ggml_is_view_op(t->op)) { + continue; + } + +- for (int i2 = 0; i2 < t->ne[2]; i2++) { +- for (int i1 = 0; i1 < t->ne[1]; i1++) { +- // generate a shuffled subset of row indices +- std::vector data(ne[1]); +- for (int i = 0; i < ne[1]; i++) { +- data[i] = i; +- } +- std::shuffle(data.begin(), data.end(), rng); +- data.resize(t->ne[0]); +- +- const size_t offs = i1*t->nb[1] + i2*t->nb[2]; +- if (t->type == GGML_TYPE_I32) { +- // TODO: Make a template or something +- std::vector data_i32(t->ne[0]); +- for (int i = 0; i < t->ne[0]; i++) { +- data_i32[i] = static_cast(data[i]); +- } +- ggml_backend_tensor_set(t, data_i32.data(), offs, t->ne[0]*sizeof(int32_t)); +- } else { +- ggml_backend_tensor_set(t, data.data(), offs, t->ne[0]*sizeof(int64_t)); +- } +- } +- } ++ init_set_rows_row_ids(t, ne[1]); + } else { + init_tensor_uniform(t); + } +@@ -2207,6 +2211,67 @@ struct test_set_rows : public test_case { + } + }; + ++// GGML_OP_ROPE + GGML_OP_VIEW + GGML_OP_SET_ROWS ++struct test_rope_set_rows : public test_case { ++ const ggml_type type; ++ const ggml_type type_idx; ++ const std::array ne; ++ int mode; ++ ++ std::string vars() override { ++ return VARS_TO_STR4(type, type_idx, ne, mode); ++ } ++ ++ std::string op_desc(ggml_tensor * t) override { ++ GGML_UNUSED(t); ++ return "ROPE_SET_ROWS"; ++ } ++ ++ bool run_whole_graph() override { return true; } ++ ++ test_rope_set_rows(ggml_type type, ++ ggml_type type_idx, ++ std::array ne, ++ int mode) ++ : type(type), type_idx(type_idx), ne(ne), mode(mode) {} ++ ++ ggml_tensor * build_graph(ggml_context * ctx) override { ++ ggml_tensor * src = ggml_new_tensor_4d(ctx, GGML_TYPE_F32, ne[0], ne[1], ne[2], 1); ++ ggml_set_name(src, "src"); ++ ++ ggml_tensor * pos = ggml_new_tensor_1d(ctx, GGML_TYPE_I32, ne[2]); ++ ++ ggml_tensor * rope = ggml_rope(ctx, src, pos, ne[0], mode); ++ ++ ggml_tensor * view = ggml_view_2d(ctx, rope, ne[0] * ne[1], ne[2], rope->nb[2], 0); ++ ++ ggml_tensor * dst = ggml_new_tensor_4d(ctx, type, ne[0] * ne[1], ne[2] * ne[3], 1, 1); ++ ggml_set_name(dst, "dst"); ++ ++ ggml_tensor * row_idxs = ggml_new_tensor_3d(ctx, type_idx, ne[2], 1, 1); ++ ggml_set_name(row_idxs, "row_idxs"); ++ ++ ggml_tensor * out = ggml_set_rows(ctx, dst, view, row_idxs); ++ ggml_set_name(out, "out"); ++ ++ return out; ++ } ++ ++ void initialize_tensors(ggml_context * ctx) override { ++ for (ggml_tensor * t = ggml_get_first_tensor(ctx); t != NULL; t = ggml_get_next_tensor(ctx, t)) { ++ if (t->type == GGML_TYPE_I64 || t->type == GGML_TYPE_I32) { ++ if (ggml_is_view_op(t->op)) { ++ continue; ++ } ++ ++ init_set_rows_row_ids(t, ne[2]); ++ } else { ++ init_tensor_uniform(t); ++ } ++ } ++ } ++}; ++ + // GGML_OP_ARGMAX + struct test_argmax : public test_case { + const ggml_type type; +@@ -6008,6 +6073,13 @@ static std::vector> make_test_cases_eval() { + } + } + ++ for (int mode : { GGML_ROPE_TYPE_NORMAL, GGML_ROPE_TYPE_NEOX }) { ++ for (ggml_type type : {GGML_TYPE_F16, GGML_TYPE_F32}) { ++ test_cases.emplace_back(new test_rope_set_rows(type, GGML_TYPE_I64, { 128, 32, 1, 100 }, mode)); ++ test_cases.emplace_back(new test_rope_set_rows(type, GGML_TYPE_I64, { 128, 32, 512, 1 }, mode)); ++ } ++ } ++ + for (ggml_type type_input : {GGML_TYPE_F32}) { + for (ggml_op_pool pool_type : {GGML_OP_POOL_AVG, GGML_OP_POOL_MAX}) { + for (int k0 : {1, 3}) { diff --git a/llama/patches/0033-vulkan-Handle-argsort-with-a-large-number-of-rows-16.patch b/llama/patches/0033-vulkan-Handle-argsort-with-a-large-number-of-rows-16.patch new file mode 100644 index 00000000..27a50a5f --- /dev/null +++ b/llama/patches/0033-vulkan-Handle-argsort-with-a-large-number-of-rows-16.patch @@ -0,0 +1,85 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jeff Bolz +Date: Thu, 30 Oct 2025 01:27:41 -0500 +Subject: [PATCH] vulkan: Handle argsort with a large number of rows (#16851) + +--- + ggml/src/ggml-vulkan/ggml-vulkan.cpp | 4 ++++ + ggml/src/ggml-vulkan/vulkan-shaders/argsort.comp | 16 ++++++++++++---- + 2 files changed, 16 insertions(+), 4 deletions(-) + +diff --git a/ggml/src/ggml-vulkan/ggml-vulkan.cpp b/ggml/src/ggml-vulkan/ggml-vulkan.cpp +index aaf4334b5..3604ceb04 100644 +--- a/ggml/src/ggml-vulkan/ggml-vulkan.cpp ++++ b/ggml/src/ggml-vulkan/ggml-vulkan.cpp +@@ -1084,6 +1084,7 @@ struct vk_op_soft_max_push_constants { + + struct vk_op_argsort_push_constants { + uint32_t ncols; ++ uint32_t nrows; + int32_t order; + }; + +@@ -8710,6 +8711,7 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co + break; + case GGML_OP_ARGSORT: + elements = { (uint32_t)ne00, (uint32_t)ggml_nrows(src0), 1 }; ++ elements[1] = std::min(elements[1], ctx->device->properties.limits.maxComputeWorkGroupCount[1]); + break; + case GGML_OP_IM2COL: + { +@@ -9952,9 +9954,11 @@ static void ggml_vk_argsort(ggml_backend_vk_context * ctx, vk_context& subctx, c + int32_t * op_params = (int32_t *)dst->op_params; + + uint32_t ncols = src0->ne[0]; ++ uint32_t nrows = ggml_nrows(src0); + + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_ARGSORT, { + ncols, ++ nrows, + op_params[0], + }, dryrun); + } +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/argsort.comp b/ggml/src/ggml-vulkan/vulkan-shaders/argsort.comp +index c81b84452..c4e68bc02 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/argsort.comp ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/argsort.comp +@@ -14,6 +14,7 @@ layout (binding = 1) buffer D {int data_d[];}; + + layout (push_constant) uniform parameter { + uint ncols; ++ uint nrows; + uint order; + } p; + +@@ -26,10 +27,9 @@ void swap(uint idx0, uint idx1) { + dst_row[idx1] = tmp; + } + +-void argsort(bool needs_bounds_check) { ++void argsort(bool needs_bounds_check, const uint row) { + // bitonic sort + const int col = int(gl_LocalInvocationID.x); +- const uint row = gl_WorkGroupID.y; + + const uint row_offset = row * p.ncols; + +@@ -72,8 +72,16 @@ void argsort(bool needs_bounds_check) { + + void main() { + if (p.ncols == BLOCK_SIZE) { +- argsort(false); ++ uint row = gl_WorkGroupID.y; ++ while (row < p.nrows) { ++ argsort(false, row); ++ row += gl_WorkGroupSize.y * gl_NumWorkGroups.y; ++ } + } else { +- argsort(true); ++ uint row = gl_WorkGroupID.y; ++ while (row < p.nrows) { ++ argsort(true, row); ++ row += gl_WorkGroupSize.y * gl_NumWorkGroups.y; ++ } + } + } diff --git a/llama/patches/0034-vulkan-fix-shmem-overrun-in-mmq-id-shader-16873.patch b/llama/patches/0034-vulkan-fix-shmem-overrun-in-mmq-id-shader-16873.patch new file mode 100644 index 00000000..73dad676 --- /dev/null +++ b/llama/patches/0034-vulkan-fix-shmem-overrun-in-mmq-id-shader-16873.patch @@ -0,0 +1,77 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ruben Ortlam +Date: Fri, 31 Oct 2025 08:14:49 +0100 +Subject: [PATCH] vulkan: fix shmem overrun in mmq id shader (#16873) + +* vulkan: fix shmem overrun in mmq id shader + +* metal : fix mul_mm_id + +--------- + +Co-authored-by: Georgi Gerganov +--- + ggml/src/ggml-metal/ggml-metal-device.cpp | 2 +- + ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq.comp | 4 ++++ + ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq_shmem_types.glsl | 2 +- + tests/test-backend-ops.cpp | 3 +++ + 4 files changed, 9 insertions(+), 2 deletions(-) + +diff --git a/ggml/src/ggml-metal/ggml-metal-device.cpp b/ggml/src/ggml-metal/ggml-metal-device.cpp +index 758116342..c78082ac3 100644 +--- a/ggml/src/ggml-metal/ggml-metal-device.cpp ++++ b/ggml/src/ggml-metal/ggml-metal-device.cpp +@@ -677,7 +677,7 @@ ggml_metal_pipeline_t ggml_metal_library_get_pipeline_mul_mm_id_map0(ggml_metal_ + char name[256]; + + snprintf(base, 256, "kernel_mul_mm_id_map0_ne20_%d", ne20); +- snprintf(name, 256, "%s", base); ++ snprintf(name, 256, "%s_ne02=%d", base, ne02); + + ggml_metal_pipeline_t res = ggml_metal_library_get_pipeline(lib, name); + if (res) { +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq.comp b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq.comp +index 8b238ac4b..d955b4fc7 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq.comp ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq.comp +@@ -82,9 +82,13 @@ layout (constant_id = 10) const uint WARP = 32; + + #include "mul_mmq_shmem_types.glsl" + ++#ifdef MUL_MAT_ID ++#define BK_STEP 1 ++#else + #ifndef BK_STEP + #define BK_STEP 4 + #endif ++#endif + + // Shared memory cache + shared block_a_cache buf_a[BM * BK_STEP]; +diff --git a/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq_shmem_types.glsl b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq_shmem_types.glsl +index 72fec4404..1c0f5306f 100644 +--- a/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq_shmem_types.glsl ++++ b/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq_shmem_types.glsl +@@ -27,7 +27,7 @@ struct block_a_cache { + #elif defined(DATA_A_Q8_0) + #define QUANT_R_MMQ 1 + // AMD likes 4, Intel likes 1 and Nvidia likes 2 +-#define BK_STEP 1 ++// #define BK_STEP 1 + struct block_a_cache { + int32_t qs[32/4]; + FLOAT_TYPE dm; +diff --git a/tests/test-backend-ops.cpp b/tests/test-backend-ops.cpp +index 657b6cc2f..1f8dda383 100644 +--- a/tests/test-backend-ops.cpp ++++ b/tests/test-backend-ops.cpp +@@ -6722,6 +6722,9 @@ static std::vector> make_test_cases_eval() { + test_cases.emplace_back(new test_mul_mat_id(GGML_TYPE_F16, GGML_TYPE_F32, 1, 1, false, 8, 16, 1)); + test_cases.emplace_back(new test_mul_mat_id(GGML_TYPE_F16, GGML_TYPE_F32, 16, 16, false, 32, 32, 32, 3)); + ++ // gpt-oss issue with Vulkan mmq_id ++ test_cases.emplace_back(new test_mul_mat_id(GGML_TYPE_MXFP4, GGML_TYPE_F32, 32, 2, false, 2880, 32, 2880)); ++ + for (ggml_type type_a : base_types) { + for (ggml_type type_b : {GGML_TYPE_F32 /*, GGML_TYPE_F16 */}) { + for (int n_mats : {4, 8}) { diff --git a/llama/patches/0035-vulkan-Fix-crash-when-FP16-mul_mat-accumulation-is-n.patch b/llama/patches/0035-vulkan-Fix-crash-when-FP16-mul_mat-accumulation-is-n.patch new file mode 100644 index 00000000..dfa46916 --- /dev/null +++ b/llama/patches/0035-vulkan-Fix-crash-when-FP16-mul_mat-accumulation-is-n.patch @@ -0,0 +1,80 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Masato Nakasaka +Date: Fri, 31 Oct 2025 16:18:59 +0900 +Subject: [PATCH] vulkan: Fix crash when FP16 mul_mat accumulation is not + supported (#16796) + +* Experimenting crash fix + +* added assert for aborting and fixed comment + +* changed to check if a pipeline is empty or not + +* Moved function in class definition + +* replaced with is_empty + +* Modified is_empty to check only unaligned pipelines +--- + ggml/src/ggml-vulkan/ggml-vulkan.cpp | 20 +++++++++++++------- + 1 file changed, 13 insertions(+), 7 deletions(-) + +diff --git a/ggml/src/ggml-vulkan/ggml-vulkan.cpp b/ggml/src/ggml-vulkan/ggml-vulkan.cpp +index 3604ceb04..80185d9f0 100644 +--- a/ggml/src/ggml-vulkan/ggml-vulkan.cpp ++++ b/ggml/src/ggml-vulkan/ggml-vulkan.cpp +@@ -146,8 +146,13 @@ static void ggml_vk_destroy_pipeline(vk::Device& device, vk_pipeline& pipeline); + struct vk_matmul_pipeline_struct { + vk_pipeline l, m, s; + vk_pipeline a_l, a_m, a_s; ++ // Returns true when all unaligned pipelines are null. ++ // We only check for unaligned variants since one of the unaligned pipelines must exist ++ // while aligned pipelines are optional ++ bool is_empty() const { ++ return l == nullptr && m == nullptr && s == nullptr; ++ } + }; +- + typedef std::shared_ptr vk_matmul_pipeline; + + struct vk_matmul_pipeline2 { +@@ -5080,7 +5085,7 @@ static vk_matmul_pipeline ggml_vk_get_mul_mat_mat_pipeline(ggml_backend_vk_conte + if (src1_type == GGML_TYPE_Q8_1) { + vk_matmul_pipeline pipelines = ctx->device->pipeline_dequant_mul_mat_mat_q8_1[src0_type].f32acc; + +- if (pipelines->s == nullptr && pipelines->m == nullptr && pipelines->l == nullptr) { ++ if (pipelines->is_empty()) { + return nullptr; + } + +@@ -5229,7 +5234,7 @@ static vk_matmul_pipeline ggml_vk_get_mul_mat_mat_id_pipeline(ggml_backend_vk_co + if (src1_type == GGML_TYPE_Q8_1) { + vk_matmul_pipeline pipelines = ctx->device->pipeline_dequant_mul_mat_mat_id_q8_1[src0_type].f32acc; + +- if (pipelines->s == nullptr && pipelines->m == nullptr && pipelines->l == nullptr) { ++ if (pipelines->is_empty()) { + return nullptr; + } + +@@ -5264,16 +5269,17 @@ static vk_matmul_pipeline ggml_vk_get_mul_mat_mat_id_pipeline(ggml_backend_vk_co + return nullptr; + } + ++ vk_matmul_pipeline2& mmp = ctx->device->pipeline_dequant_mul_mat_mat_id[src0_type]; + // XXX TODO 'prec' is not actually allowed in mul_mat_id. + bool prefer_fp16acc = ctx->device->fp16 /*&& prec == GGML_PREC_DEFAULT*/; +- bool support_fp16acc = ctx->device->pipeline_dequant_mul_mat_mat_id[src0_type].f16acc != nullptr; +- bool support_fp32acc = ctx->device->pipeline_dequant_mul_mat_mat_id[src0_type].f32acc != nullptr; ++ bool support_fp16acc = !mmp.f16acc->is_empty(); ++ bool support_fp32acc = !mmp.f32acc->is_empty(); + + if (support_fp16acc && (prefer_fp16acc || !support_fp32acc)) { +- return ctx->device->pipeline_dequant_mul_mat_mat_id[src0_type].f16acc; ++ return mmp.f16acc; + } else { + GGML_ASSERT(support_fp32acc); +- return ctx->device->pipeline_dequant_mul_mat_mat_id[src0_type].f32acc; ++ return mmp.f32acc; + } + } + diff --git a/ml/backend/ggml/ggml/src/ggml-impl.h b/ml/backend/ggml/ggml/src/ggml-impl.h index 639d551a..e5c446d1 100644 --- a/ml/backend/ggml/ggml/src/ggml-impl.h +++ b/ml/backend/ggml/ggml/src/ggml-impl.h @@ -693,6 +693,7 @@ GGML_API void ggml_dxgi_pdh_release(); #endif #ifdef __cplusplus +#include #include #include @@ -708,6 +709,21 @@ inline bool ggml_can_fuse_subgraph(const struct ggml_cgraph * cgraph, return ggml_can_fuse_subgraph(cgraph, start_idx, ops.size(), ops.begin(), outputs.begin(), outputs.size()); } +// Return true if the edges in the graph match expectations. +inline bool ggml_check_edges(const struct ggml_cgraph * cgraph, + int start_idx, + std::initializer_list> edges) { + for (const auto & edge : edges) { + int dst_node = edge[0]; + int src_idx = edge[1]; + int src_node = edge[2]; + if (cgraph->nodes[start_idx + dst_node]->src[src_idx] != cgraph->nodes[start_idx + src_node]) { + return false; + } + } + return true; +} + // expose GGUF internals for test code GGML_API size_t gguf_type_size(enum gguf_type type); GGML_API struct gguf_context * gguf_init_from_file_impl(FILE * file, struct gguf_init_params params); diff --git a/ml/backend/ggml/ggml/src/ggml-metal/ggml-metal-device.cpp b/ml/backend/ggml/ggml/src/ggml-metal/ggml-metal-device.cpp index 75811634..c78082ac 100644 --- a/ml/backend/ggml/ggml/src/ggml-metal/ggml-metal-device.cpp +++ b/ml/backend/ggml/ggml/src/ggml-metal/ggml-metal-device.cpp @@ -677,7 +677,7 @@ ggml_metal_pipeline_t ggml_metal_library_get_pipeline_mul_mm_id_map0(ggml_metal_ char name[256]; snprintf(base, 256, "kernel_mul_mm_id_map0_ne20_%d", ne20); - snprintf(name, 256, "%s", base); + snprintf(name, 256, "%s_ne02=%d", base, ne02); ggml_metal_pipeline_t res = ggml_metal_library_get_pipeline(lib, name); if (res) { diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/ggml-vulkan.cpp b/ml/backend/ggml/ggml/src/ggml-vulkan/ggml-vulkan.cpp index 221e2950..80185d9f 100644 --- a/ml/backend/ggml/ggml/src/ggml-vulkan/ggml-vulkan.cpp +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/ggml-vulkan.cpp @@ -146,8 +146,13 @@ static void ggml_vk_destroy_pipeline(vk::Device& device, vk_pipeline& pipeline); struct vk_matmul_pipeline_struct { vk_pipeline l, m, s; vk_pipeline a_l, a_m, a_s; + // Returns true when all unaligned pipelines are null. + // We only check for unaligned variants since one of the unaligned pipelines must exist + // while aligned pipelines are optional + bool is_empty() const { + return l == nullptr && m == nullptr && s == nullptr; + } }; - typedef std::shared_ptr vk_matmul_pipeline; struct vk_matmul_pipeline2 { @@ -387,12 +392,81 @@ static constexpr uint32_t num_argsort_pipelines = 11; static constexpr uint32_t max_argsort_cols = 1 << (num_argsort_pipelines-1); static constexpr uint32_t num_topk_moe_pipelines = 10; -static constexpr std::array topk_moe_norm{ GGML_OP_SOFT_MAX, GGML_OP_RESHAPE, GGML_OP_ARGSORT, - GGML_OP_VIEW, GGML_OP_GET_ROWS, GGML_OP_RESHAPE, - GGML_OP_SUM_ROWS, GGML_OP_DIV, GGML_OP_RESHAPE }; -static constexpr std::array topk_moe { GGML_OP_SOFT_MAX, GGML_OP_RESHAPE, GGML_OP_ARGSORT, - GGML_OP_VIEW, GGML_OP_GET_ROWS }; +static constexpr std::initializer_list topk_moe_early_softmax_norm{ GGML_OP_SOFT_MAX, GGML_OP_RESHAPE, GGML_OP_ARGSORT, + GGML_OP_VIEW, GGML_OP_GET_ROWS, GGML_OP_RESHAPE, + GGML_OP_SUM_ROWS, GGML_OP_CLAMP, GGML_OP_DIV, + GGML_OP_RESHAPE }; +static constexpr std::initializer_list topk_moe_early_softmax { GGML_OP_SOFT_MAX, GGML_OP_RESHAPE, GGML_OP_ARGSORT, + GGML_OP_VIEW, GGML_OP_GET_ROWS }; +static constexpr std::initializer_list topk_moe_late_softmax { GGML_OP_ARGSORT, GGML_OP_VIEW, + GGML_OP_GET_ROWS, GGML_OP_RESHAPE, + GGML_OP_SOFT_MAX, GGML_OP_RESHAPE }; +//node #978 ( SOFT_MAX): ffn_moe_probs-15 ( 0K) [Vulka ] use=2: ffn_moe_logits-15 ( 0K) [Vulka ] +//node #979 ( RESHAPE): ffn_moe_probs-15 (re ( 0K) [Vulka ] use=1: ffn_moe_probs-15 ( 0K) [Vulka ] +//node #980 ( ARGSORT): ffn_moe_argsort-15 ( 0K) [Vulka ] use=1: ffn_moe_probs-15 ( 0K) [Vulka ] +//node #981 ( VIEW): ffn_moe_topk-15 ( 0K) [Vulka ] use=4: ffn_moe_argsort-15 ( 0K) [Vulka ] +//node #982 ( GET_ROWS): ffn_moe_weights-15 ( 0K) [Vulka ] use=1: ffn_moe_probs-15 (re ( 0K) [Vulka ] ffn_moe_topk-15 ( 0K) [Vulka ] +//node #983 ( RESHAPE): ffn_moe_weights-15 ( ( 0K) [Vulka ] use=2: ffn_moe_weights-15 ( 0K) [Vulka ] +//node #984 ( SUM_ROWS): ffn_moe_weights_sum- ( 0K) [Vulka ] use=1: ffn_moe_weights-15 ( ( 0K) [Vulka ] +//node #985 ( CLAMP): ffn_moe_weights_sum_ ( 0K) [Vulka ] use=1: ffn_moe_weights_sum- ( 0K) [Vulka ] +//node #986 ( DIV): ffn_moe_weights_norm ( 0K) [Vulka ] use=1: ffn_moe_weights-15 ( ( 0K) [Vulka ] ffn_moe_weights_sum_ ( 0K) [Vulka ] +//node #987 ( RESHAPE): ffn_moe_weights_norm ( 0K) [Vulka ] use=1: ffn_moe_weights_norm ( 0K) [Vulka ] +static constexpr std::initializer_list> topk_moe_early_softmax_norm_edges { + { 1, 0, 0 }, // reshape->src[0] == softmax + { 2, 0, 0 }, // argsort->src[0] == softmax + { 3, 0, 2 }, // view->src[0] == argsort + { 4, 0, 1 }, // get_rows->src[0] == reshape + { 4, 1, 3 }, // get_rows->src[1] == view + { 5, 0, 4 }, // reshape->src[0] == get_rows + { 6, 0, 5 }, // sum_rows->src[0] == reshape + { 7, 0, 6 }, // clamp->src[0] == sum_rows + { 8, 0, 5 }, // div->src[0] == reshape + { 8, 1, 7 }, // div->src[1] == clamp + { 9, 0, 8 }, // reshape->src[0] == div +}; + +// same as early_softmax_norm but ending after the get_rows +static constexpr std::initializer_list> topk_moe_early_softmax_edges { + { 1, 0, 0 }, // reshape->src[0] == softmax + { 2, 0, 0 }, // argsort->src[0] == softmax + { 3, 0, 2 }, // view->src[0] == argsort + { 4, 0, 1 }, // get_rows->src[0] == reshape + { 4, 1, 3 }, // get_rows->src[1] == view +}; + +//node #652 ( ARGSORT): ffn_moe_argsort-11 ( 0K) [Vulka ] use=1: ffn_moe_probs-11 ( 0K) [Vulka ] +//node #653 ( VIEW): ffn_moe_topk-11 ( 0K) [Vulka ] use=7: ffn_moe_argsort-11 ( 0K) [Vulka ] +//node #654 ( GET_ROWS): ffn_moe_weights-11 ( 0K) [Vulka ] use=1: ffn_moe_probs-11 (re ( 0K) [Vulka ] ffn_moe_topk-11 ( 0K) [Vulka ] +//node #655 ( RESHAPE): ffn_moe_weights-11 ( ( 0K) [Vulka ] use=1: ffn_moe_weights-11 ( 0K) [Vulka ] +//node #656 ( SOFT_MAX): node_656 ( 0K) [Vulka ] use=1: ffn_moe_weights-11 ( ( 0K) [Vulka ] +//node #657 ( RESHAPE): ffn_moe_weights_soft ( 0K) [Vulka ] use=1: node_656 ( 0K) [Vulka ] +static constexpr std::initializer_list> topk_moe_late_softmax_edges { + { 1, 0, 0 }, // view->src[0] == argsort + { 2, 1, 1 }, // get_rows->src[1] == view + { 3, 0, 2 }, // reshape->src[0] == get_rows + { 4, 0, 3 }, // soft_max->src[0] == reshape + { 5, 0, 4 }, // reshape->src[0] == soft_max +}; + +enum topk_moe_mode { + TOPK_MOE_EARLY_SOFTMAX, + TOPK_MOE_EARLY_SOFTMAX_NORM, + TOPK_MOE_LATE_SOFTMAX, + TOPK_MOE_COUNT, +}; + +static topk_moe_mode ggml_vk_num_additional_ops_to_topk_moe_mode(uint32_t num) { + topk_moe_mode mode = num == topk_moe_early_softmax_norm.size() - 1 ? TOPK_MOE_EARLY_SOFTMAX_NORM : + num == topk_moe_early_softmax.size() - 1 ? TOPK_MOE_EARLY_SOFTMAX : + TOPK_MOE_LATE_SOFTMAX; + return mode; +} + +static constexpr std::initializer_list> rope_view_set_rows_edges { + { 1, 0, 0 }, // view->src[0] == rope + { 2, 0, 1 }, // set_rows->src[0] == view +}; struct vk_device_struct { std::recursive_mutex mutex; @@ -488,6 +562,7 @@ struct vk_device_struct { vk_matmul_pipeline2 pipeline_matmul_id_f16_f32; vk_matmul_pipeline2 pipeline_dequant_mul_mat_mat_id[GGML_TYPE_COUNT]; + vk_matmul_pipeline2 pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_COUNT]; vk_pipeline pipeline_matmul_split_k_reduce; vk_pipeline pipeline_quantize_q8_1; @@ -575,8 +650,8 @@ struct vk_device_struct { vk_pipeline pipeline_soft_max_f32, pipeline_soft_max_f32_f16; vk_pipeline pipeline_soft_max_f32_wg512, pipeline_soft_max_f32_f16_wg512; vk_pipeline pipeline_soft_max_back_f32; - vk_pipeline pipeline_rope_norm_f32, pipeline_rope_norm_f16; - vk_pipeline pipeline_rope_neox_f32, pipeline_rope_neox_f16; + vk_pipeline pipeline_rope_norm_f32, pipeline_rope_norm_f16, pipeline_rope_norm_f32_f16; + vk_pipeline pipeline_rope_neox_f32, pipeline_rope_neox_f16, pipeline_rope_neox_f32_f16; vk_pipeline pipeline_rope_multi_f32, pipeline_rope_multi_f16; vk_pipeline pipeline_rope_vision_f32, pipeline_rope_vision_f16; vk_pipeline pipeline_argsort_f32[num_argsort_pipelines]; @@ -606,8 +681,7 @@ struct vk_device_struct { vk_pipeline pipeline_flash_attn_split_k_reduce; - // [2] is {!norm, norm} - vk_pipeline pipeline_topk_moe[num_topk_moe_pipelines][2]; + vk_pipeline pipeline_topk_moe[num_topk_moe_pipelines][TOPK_MOE_COUNT]; std::vector all_pipelines; @@ -955,6 +1029,8 @@ static_assert(sizeof(vk_op_multi_add_push_constants) <= 256); struct vk_op_topk_moe_push_constants { uint32_t n_rows; uint32_t n_expert_used; + float clamp_min; + float clamp_max; }; struct vk_op_add_id_push_constants { @@ -988,6 +1064,7 @@ struct vk_op_rope_push_constants { uint32_t s2; int32_t sections[4]; uint32_t is_back; + uint32_t set_rows_stride; }; struct vk_op_soft_max_push_constants { @@ -1012,6 +1089,7 @@ struct vk_op_soft_max_push_constants { struct vk_op_argsort_push_constants { uint32_t ncols; + uint32_t nrows; int32_t order; }; @@ -1497,6 +1575,10 @@ struct ggml_backend_vk_context { // number of additional consecutive nodes that are being fused with the // node currently being processed int num_additional_fused_ops {}; + // Bitmask of which fused ops need to write an intermediate value to memory. + // Bit 'i' means nodes[start_of_fusion + i] writes to memory. + // If there's no fusion, bit 0 is still set. + int fused_ops_write_mask {}; }; static void * const vk_ptr_base = (void *)(uintptr_t) 0x1000; // NOLINT @@ -2449,8 +2531,11 @@ static void ggml_vk_load_shaders(vk_device& device) { l_warptile_id, m_warptile_id, s_warptile_id, l_warptile_mmq, m_warptile_mmq, s_warptile_mmq, l_warptile_mmq_int, m_warptile_mmq_int, s_warptile_mmq_int, + l_warptile_mmq_int_k, m_warptile_mmq_int_k, s_warptile_mmq_int_k, l_warptile_mmq_k, m_warptile_mmq_k, s_warptile_mmq_k, - l_warptile_mmqid, m_warptile_mmqid, s_warptile_mmqid; + l_warptile_mmqid, m_warptile_mmqid, s_warptile_mmqid, + l_warptile_mmqid_int, m_warptile_mmqid_int, s_warptile_mmqid_int, + l_warptile_mmqid_int_k, m_warptile_mmqid_int_k, s_warptile_mmqid_int_k; std::array l_wg_denoms, m_wg_denoms, s_wg_denoms, l_mmq_wg_denoms, m_mmq_wg_denoms, s_mmq_wg_denoms, l_mmq_wg_denoms_k, m_mmq_wg_denoms_k, s_mmq_wg_denoms_k, @@ -2513,10 +2598,16 @@ static void ggml_vk_load_shaders(vk_device& device) { m_warptile_mmq = { 128, 64, 64, 32, subgroup_size_8, 32, 2, tm_m, tn_m, tk_m, subgroup_size_8 }; s_warptile_mmq = { subgroup_size_32, 32, 32, 32, 32, 32, 2, tm_s, tn_s, tk_s, subgroup_size_8 }; + // Integer MMQ has a smaller shared memory profile, but heavier register use l_warptile_mmq_int = { 128, 128, 128, 32, subgroup_size_8 * 2, 64, 2, 4, 4, 1, subgroup_size_8 }; m_warptile_mmq_int = { 128, 64, 64, 32, subgroup_size_8, 32, 2, 2, 2, 1, subgroup_size_8 }; s_warptile_mmq_int = { subgroup_size_32, 32, 32, 32, 32, 32, 2, 2, 1, 1, subgroup_size_8 }; + // K-quants use even more registers, mitigate by setting WMITER to 1 + l_warptile_mmq_int_k = { 128, 128, 128, 32, subgroup_size_8 * 2, 64, 1, 4, 4, 1, subgroup_size_8 }; + m_warptile_mmq_int_k = { 128, 64, 64, 32, subgroup_size_8, 32, 1, 2, 2, 1, subgroup_size_8 }; + s_warptile_mmq_int_k = { subgroup_size_32, 32, 32, 32, 32, 32, 1, 2, 1, 1, subgroup_size_8 }; + l_warptile_id = { 128, 128, 128, 16, mul_mat_subgroup_size_16 * 2, 64, 2, tm_l, tn_l, tk_l, mul_mat_subgroup_size_16 }; m_warptile_id = { 128, 64, 64, 16, mul_mat_subgroup_size_16, 32, 2, tm_m, tn_m, tk_m, mul_mat_subgroup_size_16 }; s_warptile_id = { mul_mat_subgroup_size_16, 32, 32, 16, 32, 32, 2, tm_s, tn_s, tk_s, mul_mat_subgroup_size_16 }; @@ -2525,10 +2616,18 @@ static void ggml_vk_load_shaders(vk_device& device) { m_warptile_mmqid = { 128, 64, 64, 32, mul_mat_subgroup_size_8, 32, 2, tm_m, tn_m, tk_m, mul_mat_subgroup_size_8 }; s_warptile_mmqid = { mul_mat_subgroup_size_32, 32, 32, 32, 32, 32, 2, tm_s, tn_s, tk_s, mul_mat_subgroup_size_8 }; + l_warptile_mmqid_int = { 128, 128, 128, 32, mul_mat_subgroup_size_8 * 2, 64, 2, 4, 4, 1, mul_mat_subgroup_size_8 }; + m_warptile_mmqid_int = { 128, 64, 64, 32, mul_mat_subgroup_size_8, 32, 2, 2, 2, 1, mul_mat_subgroup_size_8 }; + s_warptile_mmqid_int = { mul_mat_subgroup_size_32, 32, 32, 32, 32, 32, 2, 2, 1, 1, mul_mat_subgroup_size_8 }; + + l_warptile_mmqid_int_k = { 128, 128, 128, 32, mul_mat_subgroup_size_16 * 2, 64, 1, 4, 4, 1, mul_mat_subgroup_size_16 }; + m_warptile_mmqid_int_k = { 128, 64, 64, 32, mul_mat_subgroup_size_16, 32, 1, 2, 2, 1, mul_mat_subgroup_size_16 }; + s_warptile_mmqid_int_k = { mul_mat_subgroup_size_32, 32, 32, 32, 32, 32, 1, 2, 1, 1, mul_mat_subgroup_size_16 }; + // chip specific tuning if ((device->architecture == AMD_GCN) && (device->driver_id != vk::DriverId::eAmdProprietary)) { m_warptile_mmq = m_warptile_mmq_int = { 256, 64, 64, 32, 16, 16, 2, 2, 2, 1, 16 }; - m_warptile_mmqid = { 256, 64, 64, 32, 16, 16, 2, 2, 2, 1, 16 }; + m_warptile_mmqid = m_warptile_mmqid_int = { 256, 64, 64, 32, 16, 16, 2, 2, 2, 1, 16 }; } l_mmq_wg_denoms = l_wg_denoms = {128, 128, 1 }; @@ -2913,18 +3012,15 @@ static void ggml_vk_load_shaders(vk_device& device) { if (device->mul_mat ## ID ## _s[TYPE]) \ ggml_vk_create_pipeline(device, device-> PIPELINE_NAME ->a_s, #NAMELC #F16ACC "_aligned_s", NAMELC ## _aligned ## F16ACC ## _len, NAMELC ## _aligned ## F16ACC ## _data, "main", PARAMCOUNT, sizeof(PUSHCONST), s_ ## WG_DENOMS, s_ ## WARPTILE, s_align, false, REQSUBGROUPSIZE > 0, REQSUBGROUPSIZE); \ -#define CREATE_MMQ(TYPE, PIPELINE_NAME, NAMELC, WG_DENOMS, WARPTILE, PUSHCONST, PARAMCOUNT, ID) \ +#define CREATE_MMQ(TYPE, PIPELINE_NAME, NAMELC, WG_DENOMS, WARPTILE, PUSHCONST, PARAMCOUNT, ID, REQSUBGROUPSIZE) \ if (device->mul_mat ## ID ## _l[TYPE]) { \ - ggml_vk_create_pipeline(device, device-> PIPELINE_NAME .f16acc->l, #NAMELC "_f16acc_l", NAMELC ## _f16acc_len, NAMELC ## _f16acc_data, "main", PARAMCOUNT, sizeof(PUSHCONST), l_ ## WG_DENOMS, l_ ## WARPTILE, 1); \ - ggml_vk_create_pipeline(device, device-> PIPELINE_NAME .f32acc->l, #NAMELC "_l", NAMELC ## _len, NAMELC ## _data, "main", PARAMCOUNT, sizeof(PUSHCONST), l_ ## WG_DENOMS, l_ ## WARPTILE, 1); \ + ggml_vk_create_pipeline(device, device-> PIPELINE_NAME .f32acc->l, #NAMELC "_l", NAMELC ## _len, NAMELC ## _data, "main", PARAMCOUNT, sizeof(PUSHCONST), l_ ## WG_DENOMS, l_ ## WARPTILE, 1, false, REQSUBGROUPSIZE > 0, REQSUBGROUPSIZE); \ } \ if (device->mul_mat ## ID ## _m[TYPE]) { \ - ggml_vk_create_pipeline(device, device-> PIPELINE_NAME .f16acc->m, #NAMELC "_f16acc_m", NAMELC ## _f16acc_len, NAMELC ## _f16acc_data, "main", PARAMCOUNT, sizeof(PUSHCONST), m_ ## WG_DENOMS, m_ ## WARPTILE, 1); \ - ggml_vk_create_pipeline(device, device-> PIPELINE_NAME .f32acc->m, #NAMELC "_m", NAMELC ## _len, NAMELC ## _data, "main", PARAMCOUNT, sizeof(PUSHCONST), m_ ## WG_DENOMS, m_ ## WARPTILE, 1); \ + ggml_vk_create_pipeline(device, device-> PIPELINE_NAME .f32acc->m, #NAMELC "_m", NAMELC ## _len, NAMELC ## _data, "main", PARAMCOUNT, sizeof(PUSHCONST), m_ ## WG_DENOMS, m_ ## WARPTILE, 1, false, REQSUBGROUPSIZE > 0, REQSUBGROUPSIZE); \ } \ if (device->mul_mat ## ID ## _s[TYPE]) { \ - ggml_vk_create_pipeline(device, device-> PIPELINE_NAME .f16acc->s, #NAMELC "_f16acc_s", NAMELC ## _f16acc_len, NAMELC ## _f16acc_data, "main", PARAMCOUNT, sizeof(PUSHCONST), s_ ## WG_DENOMS, s_ ## WARPTILE, 1); \ - ggml_vk_create_pipeline(device, device-> PIPELINE_NAME .f32acc->s, #NAMELC "_s", NAMELC ## _len, NAMELC ## _data, "main", PARAMCOUNT, sizeof(PUSHCONST), s_ ## WG_DENOMS, s_ ## WARPTILE, 1); \ + ggml_vk_create_pipeline(device, device-> PIPELINE_NAME .f32acc->s, #NAMELC "_s", NAMELC ## _len, NAMELC ## _data, "main", PARAMCOUNT, sizeof(PUSHCONST), s_ ## WG_DENOMS, s_ ## WARPTILE, 1, false, REQSUBGROUPSIZE > 0, REQSUBGROUPSIZE); \ } \ // Create 2 variants, {f16,f32} accumulator @@ -2963,11 +3059,19 @@ static void ggml_vk_load_shaders(vk_device& device) { #if defined(GGML_VULKAN_INTEGER_DOT_GLSLC_SUPPORT) if (device->integer_dot_product) { - CREATE_MMQ(GGML_TYPE_Q4_0, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q4_0], matmul_q4_0_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, ); - CREATE_MMQ(GGML_TYPE_Q4_1, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q4_1], matmul_q4_1_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, ); - CREATE_MMQ(GGML_TYPE_Q5_0, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q5_0], matmul_q5_0_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, ); - CREATE_MMQ(GGML_TYPE_Q5_1, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q5_1], matmul_q5_1_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, ); - CREATE_MMQ(GGML_TYPE_Q8_0, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q8_0], matmul_q8_0_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, ); + CREATE_MMQ(GGML_TYPE_Q4_0, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q4_0], matmul_q4_0_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, , 0); + CREATE_MMQ(GGML_TYPE_Q4_1, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q4_1], matmul_q4_1_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, , 0); + CREATE_MMQ(GGML_TYPE_Q5_0, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q5_0], matmul_q5_0_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, , 0); + CREATE_MMQ(GGML_TYPE_Q5_1, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q5_1], matmul_q5_1_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, , 0); + CREATE_MMQ(GGML_TYPE_Q8_0, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q8_0], matmul_q8_0_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, , 0); + + CREATE_MMQ(GGML_TYPE_MXFP4, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_MXFP4], matmul_mxfp4_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, , 0); + + CREATE_MMQ(GGML_TYPE_Q2_K, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q2_K], matmul_q2_k_q8_1, mmq_wg_denoms, warptile_mmq_int_k, vk_mat_mat_push_constants, 3, , 0); + CREATE_MMQ(GGML_TYPE_Q3_K, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q3_K], matmul_q3_k_q8_1, mmq_wg_denoms, warptile_mmq_int_k, vk_mat_mat_push_constants, 3, , 0); + CREATE_MMQ(GGML_TYPE_Q4_K, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q4_K], matmul_q4_k_q8_1, mmq_wg_denoms, warptile_mmq_int_k, vk_mat_mat_push_constants, 3, , 0); + CREATE_MMQ(GGML_TYPE_Q5_K, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q5_K], matmul_q5_k_q8_1, mmq_wg_denoms, warptile_mmq_int_k, vk_mat_mat_push_constants, 3, , 0); + CREATE_MMQ(GGML_TYPE_Q6_K, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q6_K], matmul_q6_k_q8_1, mmq_wg_denoms, warptile_mmq_int_k, vk_mat_mat_push_constants, 3, , 0); } #endif @@ -2997,6 +3101,24 @@ static void ggml_vk_load_shaders(vk_device& device) { CREATE_MM2(GGML_TYPE_IQ4_XS, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_IQ4_XS], matmul_id_subgroup_iq4_xs_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size); CREATE_MM2(GGML_TYPE_IQ4_NL, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_IQ4_NL], matmul_id_subgroup_iq4_nl_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size); CREATE_MM2(GGML_TYPE_MXFP4, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_MXFP4], matmul_id_subgroup_mxfp4_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size); + +#if defined(GGML_VULKAN_INTEGER_DOT_GLSLC_SUPPORT) + if (device->integer_dot_product) { + CREATE_MMQ(GGML_TYPE_Q4_0, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q4_0], matmul_id_subgroup_q4_0_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size); + CREATE_MMQ(GGML_TYPE_Q4_1, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q4_1], matmul_id_subgroup_q4_1_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size); + CREATE_MMQ(GGML_TYPE_Q5_0, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q5_0], matmul_id_subgroup_q5_0_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size); + CREATE_MMQ(GGML_TYPE_Q5_1, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q5_1], matmul_id_subgroup_q5_1_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size); + CREATE_MMQ(GGML_TYPE_Q8_0, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q8_0], matmul_id_subgroup_q8_0_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size); + + CREATE_MMQ(GGML_TYPE_MXFP4, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_MXFP4], matmul_id_subgroup_mxfp4_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size); + + CREATE_MMQ(GGML_TYPE_Q2_K, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q2_K], matmul_id_subgroup_q2_k_q8_1, mmq_wg_denoms, warptile_mmqid_int_k, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size_16); + CREATE_MMQ(GGML_TYPE_Q3_K, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q3_K], matmul_id_subgroup_q3_k_q8_1, mmq_wg_denoms, warptile_mmqid_int_k, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size_16); + CREATE_MMQ(GGML_TYPE_Q4_K, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q4_K], matmul_id_subgroup_q4_k_q8_1, mmq_wg_denoms, warptile_mmqid_int_k, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size_16); + CREATE_MMQ(GGML_TYPE_Q5_K, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q5_K], matmul_id_subgroup_q5_k_q8_1, mmq_wg_denoms, warptile_mmqid_int_k, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size_16); + CREATE_MMQ(GGML_TYPE_Q6_K, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q6_K], matmul_id_subgroup_q6_k_q8_1, mmq_wg_denoms, warptile_mmqid_int_k, vk_mat_mat_id_push_constants, 4, _id, mul_mat_subgroup_size_16); + } +#endif } else { CREATE_MM(GGML_TYPE_F32, pipeline_matmul_id_f32, matmul_id_f32_f32, , wg_denoms, warptile, vk_mat_mat_push_constants, 4, _id, 0); CREATE_MM2(GGML_TYPE_F16, pipeline_matmul_id_f16, matmul_id_f16, wg_denoms, warptile, vk_mat_mat_push_constants, 4, _id, 0); @@ -3023,6 +3145,24 @@ static void ggml_vk_load_shaders(vk_device& device) { CREATE_MM2(GGML_TYPE_IQ4_XS, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_IQ4_XS], matmul_id_iq4_xs_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, 4, _id, 0); CREATE_MM2(GGML_TYPE_IQ4_NL, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_IQ4_NL], matmul_id_iq4_nl_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, 4, _id, 0); CREATE_MM2(GGML_TYPE_MXFP4, pipeline_dequant_mul_mat_mat_id[GGML_TYPE_MXFP4], matmul_id_mxfp4_f32, mmq_wg_denoms, warptile_mmqid, vk_mat_mat_id_push_constants, 4, _id, 0); + +#if defined(GGML_VULKAN_INTEGER_DOT_GLSLC_SUPPORT) + if (device->integer_dot_product) { + CREATE_MMQ(GGML_TYPE_Q4_0, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q4_0], matmul_id_q4_0_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, 0); + CREATE_MMQ(GGML_TYPE_Q4_1, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q4_1], matmul_id_q4_1_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, 0); + CREATE_MMQ(GGML_TYPE_Q5_0, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q5_0], matmul_id_q5_0_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, 0); + CREATE_MMQ(GGML_TYPE_Q5_1, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q5_1], matmul_id_q5_1_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, 0); + CREATE_MMQ(GGML_TYPE_Q8_0, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q8_0], matmul_id_q8_0_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, 0); + + CREATE_MMQ(GGML_TYPE_MXFP4, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_MXFP4], matmul_id_mxfp4_q8_1, mmq_wg_denoms, warptile_mmqid_int, vk_mat_mat_id_push_constants, 4, _id, 0); + + CREATE_MMQ(GGML_TYPE_Q2_K, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q2_K], matmul_id_q2_k_q8_1, mmq_wg_denoms, warptile_mmqid_int_k, vk_mat_mat_id_push_constants, 4, _id, 0); + CREATE_MMQ(GGML_TYPE_Q3_K, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q3_K], matmul_id_q3_k_q8_1, mmq_wg_denoms, warptile_mmqid_int_k, vk_mat_mat_id_push_constants, 4, _id, 0); + CREATE_MMQ(GGML_TYPE_Q4_K, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q4_K], matmul_id_q4_k_q8_1, mmq_wg_denoms, warptile_mmqid_int_k, vk_mat_mat_id_push_constants, 4, _id, 0); + CREATE_MMQ(GGML_TYPE_Q5_K, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q5_K], matmul_id_q5_k_q8_1, mmq_wg_denoms, warptile_mmqid_int_k, vk_mat_mat_id_push_constants, 4, _id, 0); + CREATE_MMQ(GGML_TYPE_Q6_K, pipeline_dequant_mul_mat_mat_id_q8_1[GGML_TYPE_Q6_K], matmul_id_q6_k_q8_1, mmq_wg_denoms, warptile_mmqid_int_k, vk_mat_mat_id_push_constants, 4, _id, 0); + } +#endif } #undef CREATE_MM2 #undef CREATE_MMQ @@ -3087,6 +3227,12 @@ static void ggml_vk_load_shaders(vk_device& device) { CREATE_MMQ(GGML_TYPE_Q5_0, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q5_0].f32acc, matmul_q5_0_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, ); CREATE_MMQ(GGML_TYPE_Q5_1, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q5_1].f32acc, matmul_q5_1_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, ); CREATE_MMQ(GGML_TYPE_Q8_0, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q8_0].f32acc, matmul_q8_0_q8_1, mmq_wg_denoms, warptile_mmq_int, vk_mat_mat_push_constants, 3, ); + + CREATE_MMQ(GGML_TYPE_Q2_K, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q2_K].f32acc, matmul_q2_k_q8_1, mmq_wg_denoms, warptile_mmq_int_k, vk_mat_mat_push_constants, 3, ); + CREATE_MMQ(GGML_TYPE_Q3_K, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q3_K].f32acc, matmul_q3_k_q8_1, mmq_wg_denoms, warptile_mmq_int_k, vk_mat_mat_push_constants, 3, ); + CREATE_MMQ(GGML_TYPE_Q4_K, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q4_K].f32acc, matmul_q4_k_q8_1, mmq_wg_denoms, warptile_mmq_int_k, vk_mat_mat_push_constants, 3, ); + CREATE_MMQ(GGML_TYPE_Q5_K, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q5_K].f32acc, matmul_q5_k_q8_1, mmq_wg_denoms, warptile_mmq_int_k, vk_mat_mat_push_constants, 3, ); + CREATE_MMQ(GGML_TYPE_Q6_K, pipeline_dequant_mul_mat_mat_q8_1[GGML_TYPE_Q6_K].f32acc, matmul_q6_k_q8_1, mmq_wg_denoms, warptile_mmq_int_k, vk_mat_mat_push_constants, 3, ); } #endif @@ -3146,7 +3292,7 @@ static void ggml_vk_load_shaders(vk_device& device) { } // reusing CREATE_MM from the fp32 path if ((device->coopmat2 || device->coopmat_support) -#if defined(GGML_VULKAN_INTEGER_DOT_GLSLC_SUPPORT) +#if defined(GGML_VULKAN_BFLOAT16_GLSLC_SUPPORT) && !device->coopmat_bf16_support #endif ) { @@ -3567,21 +3713,27 @@ static void ggml_vk_load_shaders(vk_device& device) { ggml_vk_create_pipeline(device, device->pipeline_soft_max_f32_f16_wg512, "soft_max_f32_f16_wg512", soft_max_f32_f16_len, soft_max_f32_f16_data, "main", 4, sizeof(vk_op_soft_max_push_constants), {1, 1, 1}, { 512 }, 1); ggml_vk_create_pipeline(device, device->pipeline_soft_max_back_f32, "soft_max_back_f32", soft_max_back_f32_len, soft_max_back_f32_data, "main", 3, sizeof(vk_op_push_constants), {1, 1, 1}, { device->subgroup_size }, 1, true); - ggml_vk_create_pipeline(device, device->pipeline_rope_norm_f32, "rope_norm_f32", rope_norm_f32_len, rope_norm_f32_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); - ggml_vk_create_pipeline(device, device->pipeline_rope_neox_f32, "rope_neox_f32", rope_neox_f32_len, rope_neox_f32_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); - ggml_vk_create_pipeline(device, device->pipeline_rope_multi_f32, "rope_multi_f32", rope_multi_f32_len, rope_multi_f32_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); - ggml_vk_create_pipeline(device, device->pipeline_rope_vision_f32, "rope_vision_f32", rope_vision_f32_len, rope_vision_f32_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); + ggml_vk_create_pipeline(device, device->pipeline_rope_norm_f32, "rope_norm_f32", rope_norm_f32_len, rope_norm_f32_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); + ggml_vk_create_pipeline(device, device->pipeline_rope_neox_f32, "rope_neox_f32", rope_neox_f32_len, rope_neox_f32_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); + ggml_vk_create_pipeline(device, device->pipeline_rope_multi_f32, "rope_multi_f32", rope_multi_f32_len, rope_multi_f32_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); + ggml_vk_create_pipeline(device, device->pipeline_rope_vision_f32, "rope_vision_f32", rope_vision_f32_len, rope_vision_f32_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); if (device->float_controls_rte_fp16) { - ggml_vk_create_pipeline(device, device->pipeline_rope_norm_f16, "rope_norm_f16", rope_norm_f16_rte_len, rope_norm_f16_rte_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); - ggml_vk_create_pipeline(device, device->pipeline_rope_neox_f16, "rope_neox_f16", rope_neox_f16_rte_len, rope_neox_f16_rte_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); - ggml_vk_create_pipeline(device, device->pipeline_rope_multi_f16, "rope_multi_f16", rope_multi_f16_rte_len, rope_multi_f16_rte_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); - ggml_vk_create_pipeline(device, device->pipeline_rope_vision_f16, "rope_vision_f16", rope_vision_f16_rte_len, rope_vision_f16_rte_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); + ggml_vk_create_pipeline(device, device->pipeline_rope_norm_f16, "rope_norm_f16", rope_norm_f16_rte_len, rope_norm_f16_rte_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); + ggml_vk_create_pipeline(device, device->pipeline_rope_neox_f16, "rope_neox_f16", rope_neox_f16_rte_len, rope_neox_f16_rte_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); + ggml_vk_create_pipeline(device, device->pipeline_rope_multi_f16, "rope_multi_f16", rope_multi_f16_rte_len, rope_multi_f16_rte_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); + ggml_vk_create_pipeline(device, device->pipeline_rope_vision_f16, "rope_vision_f16", rope_vision_f16_rte_len, rope_vision_f16_rte_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); + + ggml_vk_create_pipeline(device, device->pipeline_rope_norm_f32_f16, "rope_norm_f32_f16", rope_norm_f32_f16_rte_len, rope_norm_f32_f16_rte_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); + ggml_vk_create_pipeline(device, device->pipeline_rope_neox_f32_f16, "rope_neox_f32_f16", rope_neox_f32_f16_rte_len, rope_neox_f32_f16_rte_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); } else { - ggml_vk_create_pipeline(device, device->pipeline_rope_norm_f16, "rope_norm_f16", rope_norm_f16_len, rope_norm_f16_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); - ggml_vk_create_pipeline(device, device->pipeline_rope_neox_f16, "rope_neox_f16", rope_neox_f16_len, rope_neox_f16_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); - ggml_vk_create_pipeline(device, device->pipeline_rope_multi_f16, "rope_multi_f16", rope_multi_f16_len, rope_multi_f16_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); - ggml_vk_create_pipeline(device, device->pipeline_rope_vision_f16, "rope_vision_f16", rope_vision_f16_len, rope_vision_f16_data, "main", 4, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); + ggml_vk_create_pipeline(device, device->pipeline_rope_norm_f16, "rope_norm_f16", rope_norm_f16_len, rope_norm_f16_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); + ggml_vk_create_pipeline(device, device->pipeline_rope_neox_f16, "rope_neox_f16", rope_neox_f16_len, rope_neox_f16_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); + ggml_vk_create_pipeline(device, device->pipeline_rope_multi_f16, "rope_multi_f16", rope_multi_f16_len, rope_multi_f16_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); + ggml_vk_create_pipeline(device, device->pipeline_rope_vision_f16, "rope_vision_f16", rope_vision_f16_len, rope_vision_f16_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); + + ggml_vk_create_pipeline(device, device->pipeline_rope_norm_f32_f16, "rope_norm_f32_f16", rope_norm_f32_f16_len, rope_norm_f32_f16_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); + ggml_vk_create_pipeline(device, device->pipeline_rope_neox_f32_f16, "rope_neox_f32_f16", rope_neox_f32_f16_len, rope_neox_f32_f16_data, "main", 5, sizeof(vk_op_rope_push_constants), {1, 512, 1}, {}, 1); } for (uint32_t i = 0; i < num_argsort_pipelines; ++i) { @@ -3741,8 +3893,9 @@ static void ggml_vk_load_shaders(vk_device& device) { ggml_vk_create_pipeline(device, device->pipeline_conv2d_dw_cwhn_f16_f32, "conv2d_dw_cwhn_f16_f32", conv2d_dw_cwhn_f16_f32_len, conv2d_dw_cwhn_f16_f32_data, "main", 3, sizeof(vk_op_conv2d_dw_push_constants), {512, 1, 1}, {}, 1); for (uint32_t i = 0; i < num_topk_moe_pipelines; ++i) { - ggml_vk_create_pipeline2(device, device->pipeline_topk_moe[i][0], "topk_moe_f32_"+std::to_string(i), topk_moe_f32_len, topk_moe_f32_data, "main", 3, sizeof(vk_op_topk_moe_push_constants), {1, 1, 1}, {device->subgroup_size, 1u<pipeline_topk_moe[i][1], "topk_moe_f32_"+std::to_string(i), topk_moe_f32_len, topk_moe_f32_data, "main", 3, sizeof(vk_op_topk_moe_push_constants), {1, 1, 1}, {device->subgroup_size, 1u<pipeline_topk_moe[i][TOPK_MOE_EARLY_SOFTMAX], "topk_moe_f32_early_softmax_"+std::to_string(i), topk_moe_f32_len, topk_moe_f32_data, "main", 3, sizeof(vk_op_topk_moe_push_constants), {1, 1, 1}, {device->subgroup_size, 1u<pipeline_topk_moe[i][TOPK_MOE_EARLY_SOFTMAX_NORM], "topk_moe_f32_early_softmax_norm"+std::to_string(i), topk_moe_f32_len, topk_moe_f32_data, "main", 3, sizeof(vk_op_topk_moe_push_constants), {1, 1, 1}, {device->subgroup_size, 1u<pipeline_topk_moe[i][TOPK_MOE_LATE_SOFTMAX], "topk_moe_f32_late_softmax"+std::to_string(i), topk_moe_f32_len, topk_moe_f32_data, "main", 3, sizeof(vk_op_topk_moe_push_constants), {1, 1, 1}, {device->subgroup_size, 1u<device->fp16 && prec == GGML_PREC_DEFAULT) ? ctx->device->pipeline_dequant_mul_mat_mat_q8_1[src0_type].f16acc : ctx->device->pipeline_dequant_mul_mat_mat_q8_1[src0_type].f32acc; + vk_matmul_pipeline pipelines = ctx->device->pipeline_dequant_mul_mat_mat_q8_1[src0_type].f32acc; - if (pipelines->s == nullptr && pipelines->m == nullptr && pipelines->l == nullptr) { + if (pipelines->is_empty()) { return nullptr; } @@ -5077,6 +5230,17 @@ static vk_matmul_pipeline ggml_vk_get_mul_mat_mat_id_pipeline(ggml_backend_vk_co } } + // MMQ + if (src1_type == GGML_TYPE_Q8_1) { + vk_matmul_pipeline pipelines = ctx->device->pipeline_dequant_mul_mat_mat_id_q8_1[src0_type].f32acc; + + if (pipelines->is_empty()) { + return nullptr; + } + + return pipelines; + } + GGML_ASSERT(src1_type == GGML_TYPE_F32 || (ctx->device->coopmat2 && src1_type == GGML_TYPE_F16)); switch (src0_type) { @@ -5105,16 +5269,17 @@ static vk_matmul_pipeline ggml_vk_get_mul_mat_mat_id_pipeline(ggml_backend_vk_co return nullptr; } + vk_matmul_pipeline2& mmp = ctx->device->pipeline_dequant_mul_mat_mat_id[src0_type]; // XXX TODO 'prec' is not actually allowed in mul_mat_id. bool prefer_fp16acc = ctx->device->fp16 /*&& prec == GGML_PREC_DEFAULT*/; - bool support_fp16acc = ctx->device->pipeline_dequant_mul_mat_mat_id[src0_type].f16acc != nullptr; - bool support_fp32acc = ctx->device->pipeline_dequant_mul_mat_mat_id[src0_type].f32acc != nullptr; + bool support_fp16acc = !mmp.f16acc->is_empty(); + bool support_fp32acc = !mmp.f32acc->is_empty(); if (support_fp16acc && (prefer_fp16acc || !support_fp32acc)) { - return ctx->device->pipeline_dequant_mul_mat_mat_id[src0_type].f16acc; + return mmp.f16acc; } else { GGML_ASSERT(support_fp32acc); - return ctx->device->pipeline_dequant_mul_mat_mat_id[src0_type].f32acc; + return mmp.f32acc; } } @@ -5654,14 +5819,11 @@ static void ggml_vk_buffer_copy(vk_buffer& dst, size_t dst_offset, vk_buffer& sr VK_LOG_DEBUG("ggml_vk_buffer_copy(MULTI_DEVICE, " << size << ")"); // Copy device to device ggml_vk_ensure_sync_staging_buffer(src->device, size); - ggml_vk_ensure_sync_staging_buffer(dst->device, size); // Copy to src staging buffer ggml_vk_buffer_copy(src->device->sync_staging, 0, src, src_offset, size); - // memcpy to dst staging buffer - memcpy(dst->device->sync_staging->ptr, src->device->sync_staging->ptr, size); // Copy to dst buffer - ggml_vk_buffer_copy(dst, dst_offset, dst->device->sync_staging, 0, size); + ggml_vk_buffer_write_2d(dst, dst_offset, src->device->sync_staging->ptr, 0, size, 1); } } @@ -6882,10 +7044,19 @@ static void ggml_vk_mul_mat_id_q_f16(ggml_backend_vk_context * ctx, vk_context& const bool y_f32_kernel = src1->type == GGML_TYPE_F32 && !y_non_contig; - vk_matmul_pipeline mmp = ggml_vk_get_mul_mat_mat_id_pipeline(ctx, src0->type, y_non_contig ? f16_type : src1->type, (ggml_prec)dst->op_params[0]); + bool quantize_y = ctx->device->integer_dot_product && src1->type == GGML_TYPE_F32 && ggml_is_contiguous(src1) && (ne11 * ne10) % 4 == 0; + + // Check for mmq first + vk_matmul_pipeline mmp = quantize_y ? ggml_vk_get_mul_mat_mat_id_pipeline(ctx, src0->type, GGML_TYPE_Q8_1, (ggml_prec)dst->op_params[0]) : nullptr; + + if (mmp == nullptr) { + // Fall back to f16 dequant mul mat + mmp = ggml_vk_get_mul_mat_mat_id_pipeline(ctx, src0->type, y_non_contig ? f16_type : src1->type, (ggml_prec)dst->op_params[0]); + quantize_y = false; + } const bool qx_needs_dequant = mmp == nullptr || x_non_contig; - const bool qy_needs_dequant = (src1->type != f16_type && !y_f32_kernel) || y_non_contig; + const bool qy_needs_dequant = !quantize_y && ((src1->type != f16_type && !y_f32_kernel) || y_non_contig); if (qx_needs_dequant) { // Fall back to dequant + f16 mulmat @@ -6895,8 +7066,8 @@ static void ggml_vk_mul_mat_id_q_f16(ggml_backend_vk_context * ctx, vk_context& // Not implemented GGML_ASSERT(y_non_contig || !qy_needs_dequant); // NOLINT - const uint32_t kpad = ggml_vk_align_size(ne10, ggml_vk_guess_matmul_id_pipeline_align(ctx, mmp, ne01, nei1, qx_needs_dequant ? f16_type : src0->type)); - const bool aligned = ne10 == kpad && ne01 > 8 && nei1 > 8; + const uint32_t kpad = quantize_y ? 0 : ggml_vk_align_size(ne10, ggml_vk_guess_matmul_id_pipeline_align(ctx, mmp, ne01, nei1, qx_needs_dequant ? f16_type : src0->type)); + const bool aligned = !quantize_y && ne10 == kpad && ne01 > 8 && nei1 > 8; vk_pipeline pipeline = ggml_vk_guess_matmul_id_pipeline(ctx, mmp, ne01, nei1, aligned, qx_needs_dequant ? f16_type : src0->type); @@ -6909,12 +7080,13 @@ static void ggml_vk_mul_mat_id_q_f16(ggml_backend_vk_context * ctx, vk_context& const uint64_t qx_sz = ggml_type_size(src0->type) * x_ne / ggml_blck_size(src0->type); const uint64_t qy_sz = ggml_type_size(src1->type) * y_ne / ggml_blck_size(src1->type); const uint64_t x_sz = !qx_needs_dequant ? qx_sz : sizeof(ggml_fp16_t) * x_ne; - const uint64_t y_sz = y_f32_kernel ? sizeof(float) * y_ne : sizeof(ggml_fp16_t) * y_ne; + const uint64_t y_sz = quantize_y ? (y_ne * ggml_type_size(GGML_TYPE_Q8_1) / ggml_blck_size(GGML_TYPE_Q8_1)) : (y_f32_kernel ? sizeof(float) * y_ne : sizeof(ggml_fp16_t) * y_ne); const uint64_t ids_sz = nbi2; const uint64_t d_sz = sizeof(float) * d_ne; vk_pipeline to_fp16_vk_0 = nullptr; vk_pipeline to_fp16_vk_1 = nullptr; + vk_pipeline to_q8_1 = nullptr; if (x_non_contig) { to_fp16_vk_0 = ggml_vk_get_cpy_pipeline(ctx, src0, nullptr, f16_type); @@ -6929,9 +7101,16 @@ static void ggml_vk_mul_mat_id_q_f16(ggml_backend_vk_context * ctx, vk_context& GGML_ASSERT(!qx_needs_dequant || to_fp16_vk_0 != nullptr); // NOLINT GGML_ASSERT(!qy_needs_dequant || to_fp16_vk_1 != nullptr); // NOLINT + if (quantize_y) { + to_q8_1 = ggml_vk_get_quantize_pipeline(ctx, GGML_TYPE_Q8_1, true); + } + if (dryrun) { const uint64_t x_sz_upd = x_sz * ne02 * ne03; - const uint64_t y_sz_upd = y_sz * ne12 * ne13; + uint64_t y_sz_upd = y_sz * ne12 * ne13; + if (quantize_y) { + y_sz_upd = CEIL_DIV(y_sz_upd, 144) * 144; + } if ( (qx_needs_dequant && x_sz_upd > ctx->device->properties.limits.maxStorageBufferRange) || (qy_needs_dequant && y_sz_upd > ctx->device->properties.limits.maxStorageBufferRange)) { @@ -6940,7 +7119,7 @@ static void ggml_vk_mul_mat_id_q_f16(ggml_backend_vk_context * ctx, vk_context& if (qx_needs_dequant && ctx->prealloc_size_x < x_sz_upd) { ctx->prealloc_size_x = x_sz_upd; } - if (qy_needs_dequant && ctx->prealloc_size_y < y_sz_upd) { + if ((qy_needs_dequant || quantize_y) && ctx->prealloc_size_y < y_sz_upd) { ctx->prealloc_size_y = y_sz_upd; } @@ -6952,6 +7131,9 @@ static void ggml_vk_mul_mat_id_q_f16(ggml_backend_vk_context * ctx, vk_context& if (qy_needs_dequant) { ggml_pipeline_request_descriptor_sets(ctx, to_fp16_vk_1, 1); } + if (quantize_y) { + ggml_pipeline_request_descriptor_sets(ctx, to_q8_1, 1); + } return; } @@ -6988,6 +7170,9 @@ static void ggml_vk_mul_mat_id_q_f16(ggml_backend_vk_context * ctx, vk_context& if (qy_needs_dequant) { d_Y = ctx->prealloc_y; GGML_ASSERT(d_Y->size >= y_sz * ne12 * ne13); + } else if (quantize_y) { + d_Y = ctx->prealloc_y; + GGML_ASSERT(d_Y->size >= CEIL_DIV(y_sz * ne12 * ne13, 144) * 144); } else { d_Y = d_Qy; y_buf_offset = qy_buf_offset; @@ -7019,6 +7204,17 @@ static void ggml_vk_mul_mat_id_q_f16(ggml_backend_vk_context * ctx, vk_context& ctx->prealloc_y_last_tensor_used = src1; } } + if (quantize_y) { + if (ctx->prealloc_y_last_pipeline_used != to_q8_1.get() || + ctx->prealloc_y_last_tensor_used != src1) { + if (ctx->prealloc_y_need_sync) { + ggml_vk_sync_buffers(ctx, subctx); + } + ggml_vk_quantize_q8_1(ctx, subctx, ggml_vk_subbuffer(ctx, d_Qy, qy_buf_offset), ggml_vk_subbuffer(ctx, d_Y, 0), y_ne * ne12 * ne13, true); + ctx->prealloc_y_last_pipeline_used = to_q8_1.get(); + ctx->prealloc_y_last_tensor_used = src1; + } + } uint32_t stride_batch_x = ne00*ne01; uint32_t stride_batch_y = ne10*ne11; @@ -7027,14 +7223,19 @@ static void ggml_vk_mul_mat_id_q_f16(ggml_backend_vk_context * ctx, vk_context& stride_batch_x = src0->nb[0] / ggml_type_size(src0->type); } - if (!ggml_vk_dim01_contiguous(src1) && !qy_needs_dequant) { + if (!ggml_vk_dim01_contiguous(src1) && !qy_needs_dequant && !quantize_y) { stride_batch_y = src1->nb[0] / ggml_type_size(src1->type); } + uint32_t y_sz_total = y_sz * ne12 * ne13; + if (quantize_y) { + y_sz_total = CEIL_DIV(y_sz_total, 144) * 144; + } + // compute ggml_vk_matmul_id( ctx, subctx, pipeline, - { d_X, x_buf_offset, x_sz * ne02 * ne03 }, { d_Y, y_buf_offset, y_sz * ne12 * ne13 }, + { d_X, x_buf_offset, x_sz * ne02 * ne03 }, { d_Y, y_buf_offset, y_sz_total }, { d_D, d_buf_offset, d_sz * ne22 * ne23 }, { d_ids, ids_buf_offset, ids_sz }, ne01, ne21, ne10, ne10, ne10, ne01, stride_batch_x, stride_batch_y, ne20*ne21, @@ -7973,8 +8174,8 @@ static vk_pipeline ggml_vk_op_get_pipeline(ggml_backend_vk_context * ctx, const if (ctx->num_additional_fused_ops) { uint32_t idx = (uint32_t)ceilf(log2f(float(dst->ne[0]))); GGML_ASSERT(idx < num_topk_moe_pipelines); - bool with_norm = ctx->num_additional_fused_ops == topk_moe_norm.size() - 1; - return ctx->device->pipeline_topk_moe[idx][with_norm]; + topk_moe_mode mode = ggml_vk_num_additional_ops_to_topk_moe_mode(ctx->num_additional_fused_ops); + return ctx->device->pipeline_topk_moe[idx][mode]; } if (src0->type == GGML_TYPE_F32 && (src1 == nullptr || src1->type == GGML_TYPE_F32) && dst->type == GGML_TYPE_F32) { @@ -7992,7 +8193,8 @@ static vk_pipeline ggml_vk_op_get_pipeline(ggml_backend_vk_context * ctx, const case GGML_OP_ROPE: case GGML_OP_ROPE_BACK: { - const int mode = ((const int32_t *) dst->op_params)[2]; + const ggml_tensor *rope = ctx->num_additional_fused_ops == 2 ? dst->src[0]->src[0] : dst; + const int mode = ((const int32_t *) rope->op_params)[2]; const bool is_neox = mode & GGML_ROPE_TYPE_NEOX; const bool is_mrope = mode & GGML_ROPE_TYPE_MROPE; const bool is_vision = mode == GGML_ROPE_TYPE_VISION; @@ -8001,6 +8203,9 @@ static vk_pipeline ggml_vk_op_get_pipeline(ggml_backend_vk_context * ctx, const if (src0->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32) { return ctx->device->pipeline_rope_neox_f32; } + if (src0->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F16) { + return ctx->device->pipeline_rope_neox_f32_f16; + } if (src0->type == GGML_TYPE_F16 && dst->type == GGML_TYPE_F16) { return ctx->device->pipeline_rope_neox_f16; } @@ -8022,6 +8227,9 @@ static vk_pipeline ggml_vk_op_get_pipeline(ggml_backend_vk_context * ctx, const if (src0->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32) { return ctx->device->pipeline_rope_norm_f32; } + if (src0->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F16) { + return ctx->device->pipeline_rope_norm_f32_f16; + } if (src0->type == GGML_TYPE_F16 && dst->type == GGML_TYPE_F16) { return ctx->device->pipeline_rope_norm_f16; } @@ -8029,6 +8237,13 @@ static vk_pipeline ggml_vk_op_get_pipeline(ggml_backend_vk_context * ctx, const return nullptr; } case GGML_OP_ARGSORT: + if (ctx->num_additional_fused_ops) { + uint32_t idx = (uint32_t)ceilf(log2f(float(dst->ne[0]))); + GGML_ASSERT(idx < num_topk_moe_pipelines); + topk_moe_mode mode = ggml_vk_num_additional_ops_to_topk_moe_mode(ctx->num_additional_fused_ops); + return ctx->device->pipeline_topk_moe[idx][mode]; + } + if (src0->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_I32) { uint32_t idx = (uint32_t)ceilf(log2f(float(dst->ne[0]))); return ctx->device->pipeline_argsort_f32[idx]; @@ -8224,20 +8439,22 @@ static uint32_t get_misalign_bytes(ggml_backend_vk_context * ctx, const ggml_ten return ((vk_tensor_offset(t) + t->view_offs) & (ctx->device->properties.limits.minStorageBufferOffsetAlignment - 1));; } -template void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, T &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst) { +template void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, T &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, const ggml_tensor * src3, ggml_tensor * dst) { GGML_UNUSED(p); GGML_UNUSED(src0); GGML_UNUSED(src1); GGML_UNUSED(src2); + GGML_UNUSED(src3); GGML_UNUSED(dst); static_assert(!std::is_const::value, "unexpected type"); GGML_ASSERT(!src0 || get_misalign_bytes(ctx, src0) == 0); GGML_ASSERT(!src1 || get_misalign_bytes(ctx, src1) == 0); GGML_ASSERT(!src2 || get_misalign_bytes(ctx, src2) == 0); + GGML_ASSERT(!src3 || get_misalign_bytes(ctx, src3) == 0); GGML_ASSERT(!dst || get_misalign_bytes(ctx, dst) == 0); } -template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_unary_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst) { +template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_unary_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, const ggml_tensor * src3, ggml_tensor * dst) { const uint32_t a_offset = get_misalign_bytes(ctx, src0) / ggml_type_size(src0->type); const uint32_t d_offset = get_misalign_bytes(ctx, dst) / ggml_type_size(dst->type); @@ -8245,9 +8462,10 @@ template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk GGML_UNUSED(src1); GGML_UNUSED(src2); + GGML_UNUSED(src3); } -template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_sum_rows_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst) { +template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_sum_rows_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, const ggml_tensor * src3, ggml_tensor * dst) { const uint32_t a_offset = get_misalign_bytes(ctx, src0) / ggml_type_size(src0->type); const uint32_t d_offset = get_misalign_bytes(ctx, dst) / ggml_type_size(dst->type); @@ -8255,9 +8473,10 @@ template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk GGML_UNUSED(src1); GGML_UNUSED(src2); + GGML_UNUSED(src3); } -template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_pad_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst) { +template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_pad_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, const ggml_tensor * src3, ggml_tensor * dst) { const uint32_t a_offset = get_misalign_bytes(ctx, src0) / ggml_type_size(src0->type); const uint32_t d_offset = get_misalign_bytes(ctx, dst) / ggml_type_size(dst->type); @@ -8265,9 +8484,10 @@ template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk GGML_UNUSED(src1); GGML_UNUSED(src2); + GGML_UNUSED(src3); } -template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_im2col_3d_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst) { +template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_im2col_3d_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, const ggml_tensor * src3, ggml_tensor * dst) { const uint32_t a_offset = get_misalign_bytes(ctx, src1) / ggml_type_size(src1->type); const uint32_t d_offset = get_misalign_bytes(ctx, dst) / ggml_type_size(dst->type); @@ -8275,9 +8495,10 @@ template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk GGML_UNUSED(src0); GGML_UNUSED(src2); + GGML_UNUSED(src3); } -template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_binary_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst) { +template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_binary_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, const ggml_tensor * src3, ggml_tensor * dst) { const uint32_t a_offset = get_misalign_bytes(ctx, src0) / ggml_type_size(src0->type); const uint32_t b_offset = get_misalign_bytes(ctx, src1) / ggml_type_size(src1->type); const uint32_t d_offset = get_misalign_bytes(ctx, dst) / ggml_type_size(dst->type); @@ -8287,9 +8508,10 @@ template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk p.misalign_offsets = (a_offset << 16) | (b_offset << 8) | d_offset; GGML_UNUSED(src2); + GGML_UNUSED(src3); } -template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_upscale_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst) { +template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk_op_upscale_push_constants &p, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, const ggml_tensor * src3, ggml_tensor * dst) { const uint32_t a_offset = get_misalign_bytes(ctx, src0) / ggml_type_size(src0->type); const uint32_t d_offset = get_misalign_bytes(ctx, dst) / ggml_type_size(dst->type); @@ -8298,10 +8520,11 @@ template <> void init_pushconst_tensor_offsets(ggml_backend_vk_context * ctx, vk GGML_UNUSED(src1); GGML_UNUSED(src2); + GGML_UNUSED(src3); } template -static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst, ggml_op op, PC&& pc, bool dryrun = false) { +static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, const ggml_tensor * src3, ggml_tensor * dst, ggml_op op, PC&& pc, bool dryrun = false) { VK_LOG_DEBUG("ggml_vk_op_f32((" << src0 << ", name=" << src0->name << ", type=" << src0->type << ", ne0=" << src0->ne[0] << ", ne1=" << src0->ne[1] << ", ne2=" << src0->ne[2] << ", ne3=" << src0->ne[3] << ", nb0=" << src0->nb[0] << ", nb1=" << src0->nb[1] << ", nb2=" << src0->nb[2] << ", nb3=" << src0->nb[3]; if (src1 != nullptr) { std::cerr << "), (" << src1 << ", name=" << src1->name << ", type=" << src1->type << ", ne0=" << src1->ne[0] << ", ne1=" << src1->ne[1] << ", ne2=" << src1->ne[2] << ", ne3=" << src1->ne[3] << ", nb0=" << src1->nb[0] << ", nb1=" << src1->nb[1] << ", nb2=" << src1->nb[2] << ", nb3=" << src1->nb[3]; @@ -8309,6 +8532,9 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co if (src2 != nullptr) { std::cerr << "), (" << src2 << ", name=" << src2->name << ", type=" << src2->type << ", ne0=" << src2->ne[0] << ", ne1=" << src2->ne[1] << ", ne2=" << src2->ne[2] << ", ne3=" << src2->ne[3] << ", nb0=" << src2->nb[0] << ", nb1=" << src2->nb[1] << ", nb2=" << src2->nb[2] << ", nb3=" << src2->nb[3]; } + if (src3 != nullptr) { + std::cerr << "), (" << src3 << ", name=" << src3->name << ", type=" << src3->type << ", ne0=" << src3->ne[0] << ", ne1=" << src3->ne[1] << ", ne2=" << src3->ne[2] << ", ne3=" << src3->ne[3] << ", nb0=" << src3->nb[0] << ", nb1=" << src3->nb[1] << ", nb2=" << src3->nb[2] << ", nb3=" << src3->nb[3]; + } std::cerr << "), (" << dst << ", name=" << dst->name << ", type=" << dst->type << ", ne0=" << dst->ne[0] << ", ne1=" << dst->ne[1] << ", ne2=" << dst->ne[2] << ", ne3=" << dst->ne[3] << ", nb0=" << dst->nb[0] << ", nb1=" << dst->nb[1] << ", nb2=" << dst->nb[2] << ", nb3=" << dst->nb[3]; std::cerr << "), " << ggml_op_name(op) << ", " << (dryrun ? "dryrun" : "") << ")"); GGML_ASSERT(op == GGML_OP_GET_ROWS || op == GGML_OP_CPY || (!ggml_is_quantized(src0->type) && (src1 == nullptr || !ggml_is_quantized(src1->type)))); // NOLINT @@ -8335,6 +8561,13 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co const uint64_t ne23 = use_src2 ? src2->ne[3] : 0; const uint64_t ne2 = ne20 * ne21; + const bool use_src3 = src3 != nullptr; + const uint64_t ne30 = use_src3 ? src3->ne[0] : 0; + const uint64_t ne31 = use_src3 ? src3->ne[1] : 0; + const uint64_t ne32 = use_src3 ? src3->ne[2] : 0; + const uint64_t ne33 = use_src3 ? src3->ne[3] : 0; + const uint64_t ne3 = ne30 * ne31; + const uint64_t ned0 = dst->ne[0]; const uint64_t ned1 = dst->ne[1]; const uint64_t ned2 = dst->ne[2]; @@ -8365,6 +8598,7 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co ggml_backend_vk_buffer_context * src0_buf_ctx = (ggml_backend_vk_buffer_context *)src0->buffer->context; ggml_backend_vk_buffer_context * src1_buf_ctx = use_src1 ? (ggml_backend_vk_buffer_context *)src1->buffer->context : nullptr; ggml_backend_vk_buffer_context * src2_buf_ctx = use_src2 ? (ggml_backend_vk_buffer_context *)src2->buffer->context : nullptr; + ggml_backend_vk_buffer_context * src3_buf_ctx = use_src3 ? (ggml_backend_vk_buffer_context *)src3->buffer->context : nullptr; vk_buffer d_X = nullptr; size_t x_buf_offset = 0; @@ -8372,10 +8606,13 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co size_t y_buf_offset = 0; vk_buffer d_Z = nullptr; size_t z_buf_offset = 0; + vk_buffer d_W = nullptr; + size_t w_buf_offset = 0; bool src0_uma = false; bool src1_uma = false; bool src2_uma = false; + bool src3_uma = false; if (ctx->device->uma) { ggml_vk_host_get(ctx->device, src0->data, d_X, x_buf_offset); @@ -8388,6 +8625,10 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co ggml_vk_host_get(ctx->device, src2->data, d_Z, z_buf_offset); src2_uma = d_Z != nullptr; } + if (use_src3) { + ggml_vk_host_get(ctx->device, src3->data, d_W, w_buf_offset); + src3_uma = d_W != nullptr; + } } vk_buffer d_D = dst_buf_ctx->dev_buffer; @@ -8409,11 +8650,17 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co z_buf_offset = vk_tensor_offset(src2) + src2->view_offs; GGML_ASSERT(d_Z != nullptr); } + if (use_src3 && !src3_uma) { + d_W = src3_buf_ctx->dev_buffer; + w_buf_offset = vk_tensor_offset(src3) + src3->view_offs; + GGML_ASSERT(d_W != nullptr); + } // Compute misalignment offset for descriptors and store it in in push constants, then align the descriptor offsets. - init_pushconst_tensor_offsets(ctx, pc, src0, src1, src2, dst); + init_pushconst_tensor_offsets(ctx, pc, src0, src1, src2, src3, dst); x_buf_offset &= ~(ctx->device->properties.limits.minStorageBufferOffsetAlignment - 1); y_buf_offset &= ~(ctx->device->properties.limits.minStorageBufferOffsetAlignment - 1); z_buf_offset &= ~(ctx->device->properties.limits.minStorageBufferOffsetAlignment - 1); + w_buf_offset &= ~(ctx->device->properties.limits.minStorageBufferOffsetAlignment - 1); d_buf_offset &= ~(ctx->device->properties.limits.minStorageBufferOffsetAlignment - 1); std::array elements; @@ -8470,6 +8717,7 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co break; case GGML_OP_ARGSORT: elements = { (uint32_t)ne00, (uint32_t)ggml_nrows(src0), 1 }; + elements[1] = std::min(elements[1], ctx->device->properties.limits.maxComputeWorkGroupCount[1]); break; case GGML_OP_IM2COL: { @@ -8614,12 +8862,13 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co break; } - uint64_t x_sz, y_sz, z_sz, d_sz; + uint64_t x_sz, y_sz, z_sz, w_sz, d_sz; if (op_supports_incontiguous) { x_sz = ggml_nbytes(src0) + get_misalign_bytes(ctx, src0); y_sz = use_src1 ? ggml_nbytes(src1) + get_misalign_bytes(ctx, src1) : 0; z_sz = use_src2 ? ggml_nbytes(src2) + get_misalign_bytes(ctx, src2) : 0; + w_sz = use_src3 ? ggml_nbytes(src3) + get_misalign_bytes(ctx, src3) : 0; d_sz = ggml_nbytes(dst) + get_misalign_bytes(ctx, dst); if (x_buf_offset + x_sz >= d_X->size) { @@ -8631,6 +8880,9 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co if (use_src2 && z_buf_offset + z_sz >= d_Z->size) { z_sz = ggml_vk_get_max_buffer_range(ctx, d_Z, z_buf_offset); } + if (use_src3 && w_buf_offset + w_sz >= d_W->size) { + w_sz = ggml_vk_get_max_buffer_range(ctx, d_W, w_buf_offset); + } if (d_buf_offset + d_sz >= d_D->size) { d_sz = ggml_vk_get_max_buffer_range(ctx, d_D, d_buf_offset); } @@ -8638,6 +8890,7 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co x_sz = ggml_type_size(src0->type)/ggml_blck_size(src0->type) * ne0 * ne02 * ne03; y_sz = use_src1 ? ggml_type_size(src1->type) * ne1 * ne12 * ne13 : 0; z_sz = use_src2 ? ggml_type_size(src2->type) * ne2 * ne22 * ne23 : 0; + w_sz = use_src3 ? ggml_type_size(src3->type) * ne3 * ne32 * ne33 : 0; d_sz = ggml_type_size(dst->type) * ned * ned2 * ned3; } @@ -8679,14 +8932,19 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co ggml_vk_dispatch_pipeline(ctx, subctx, pipeline, { vk_subbuffer{ d_X, x_buf_offset, x_sz }, subbuf_y, subbuf_z, vk_subbuffer{ d_D, d_buf_offset, d_sz } }, pc, elements); } else if (op == GGML_OP_ROPE || op == GGML_OP_ROPE_BACK) { // Empty src2 is possible in rope, but the shader needs a buffer - vk_subbuffer subbuf_z; + vk_subbuffer subbuf_z, subbuf_w; if (use_src2) { subbuf_z = { d_Z, z_buf_offset, z_sz }; } else { subbuf_z = { d_X, 0, x_sz }; } + if (use_src3) { + subbuf_w = { d_W, w_buf_offset, w_sz }; + } else { + subbuf_w = { d_X, 0, x_sz }; + } - ggml_vk_dispatch_pipeline(ctx, subctx, pipeline, { vk_subbuffer{ d_X, x_buf_offset, x_sz }, vk_subbuffer{ d_Y, y_buf_offset, y_sz }, subbuf_z, vk_subbuffer{ d_D, d_buf_offset, d_sz } }, pc, elements); + ggml_vk_dispatch_pipeline(ctx, subctx, pipeline, { vk_subbuffer{ d_X, x_buf_offset, x_sz }, vk_subbuffer{ d_Y, y_buf_offset, y_sz }, subbuf_z, vk_subbuffer{ d_D, d_buf_offset, d_sz }, subbuf_w }, pc, elements); } else if (op == GGML_OP_IM2COL || op == GGML_OP_IM2COL_3D) { if (ctx->device->shader_int64 && ctx->device->buffer_device_address) { // buffer device address path doesn't use dst buffer @@ -8702,6 +8960,8 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co } else if (op == GGML_OP_OPT_STEP_SGD) { // OPT_STEP_SGD works on src0, it does not need dst ggml_vk_dispatch_pipeline(ctx, subctx, pipeline, { vk_subbuffer{ d_X, x_buf_offset, x_sz }, vk_subbuffer{ d_Y, y_buf_offset, y_sz }, vk_subbuffer{ d_Z, z_buf_offset, z_sz } }, pc, elements); + } else if (use_src3) { + ggml_vk_dispatch_pipeline(ctx, subctx, pipeline, { vk_subbuffer{ d_X, x_buf_offset, x_sz }, vk_subbuffer{ d_Y, y_buf_offset, y_sz }, vk_subbuffer{ d_Z, z_buf_offset, z_sz }, vk_subbuffer{ d_W, w_buf_offset, w_sz }, vk_subbuffer{ d_D, d_buf_offset, d_sz } }, pc, elements); } else if (use_src2) { ggml_vk_dispatch_pipeline(ctx, subctx, pipeline, { vk_subbuffer{ d_X, x_buf_offset, x_sz }, vk_subbuffer{ d_Y, y_buf_offset, y_sz }, vk_subbuffer{ d_Z, z_buf_offset, z_sz }, vk_subbuffer{ d_D, d_buf_offset, d_sz } }, pc, elements); } else if (use_src1) { @@ -8716,7 +8976,7 @@ static void ggml_vk_get_rows(ggml_backend_vk_context * ctx, vk_context& subctx, const uint32_t src1_type_size = ggml_type_size(src1->type); const uint32_t dst_type_size = ggml_type_size(dst->type); - ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_GET_ROWS, { + ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_GET_ROWS, { (uint32_t)ggml_nelements(src0), (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2],(uint32_t)src0->ne[3], (uint32_t)src0->nb[0] / src0_type_size, (uint32_t)src0->nb[1] / src0_type_size, (uint32_t)src0->nb[2] / src0_type_size, (uint32_t)src0->nb[3] / src0_type_size, (uint32_t)src1->ne[0], (uint32_t)src1->ne[1], (uint32_t)src1->ne[2],(uint32_t)src1->ne[3], (uint32_t)src1->nb[0] / src1_type_size, (uint32_t)src1->nb[1] / src1_type_size, (uint32_t)src1->nb[2] / src1_type_size, (uint32_t)src1->nb[3] / src1_type_size, @@ -8736,7 +8996,7 @@ static void ggml_vk_acc(ggml_backend_vk_context * ctx, vk_context& subctx, const // int nb3 = dst->op_params[2] / 4; // 4 bytes of float32 - unused int offset = dst->op_params[3] / 4; // offset in bytes - ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_ACC, { + ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_ACC, { (uint32_t)ggml_nelements(src0), (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2],(uint32_t)src0->ne[3], (uint32_t)src0->nb[0] / src0_type_size, (uint32_t)nb1, (uint32_t)nb2, (uint32_t)src0->nb[3] / src0_type_size, (uint32_t)src1->ne[0], (uint32_t)src1->ne[1], (uint32_t)src1->ne[2],(uint32_t)src1->ne[3], (uint32_t)src1->nb[0] / src1_type_size, (uint32_t)src1->nb[1] / src1_type_size, (uint32_t)src1->nb[2] / src1_type_size, (uint32_t)src1->nb[3] / src1_type_size, @@ -8861,7 +9121,7 @@ static void ggml_vk_add(ggml_backend_vk_context * ctx, vk_context& subctx, const const uint32_t src1_type_size = ggml_type_size(src1->type); const uint32_t dst_type_size = ggml_type_size(dst->type); - ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_ADD, { + ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_ADD, { (uint32_t)ggml_nelements(src0), (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2],(uint32_t)src0->ne[3], (uint32_t)src0->nb[0] / src0_type_size, (uint32_t)src0->nb[1] / src0_type_size, (uint32_t)src0->nb[2] / src0_type_size, (uint32_t)src0->nb[3] / src0_type_size, (uint32_t)src1->ne[0], (uint32_t)src1->ne[1], (uint32_t)src1->ne[2],(uint32_t)src1->ne[3], (uint32_t)src1->nb[0] / src1_type_size, (uint32_t)src1->nb[1] / src1_type_size, (uint32_t)src1->nb[2] / src1_type_size, (uint32_t)src1->nb[3] / src1_type_size, @@ -8876,7 +9136,7 @@ static void ggml_vk_sub(ggml_backend_vk_context * ctx, vk_context& subctx, const const uint32_t src1_type_size = ggml_type_size(src1->type); const uint32_t dst_type_size = ggml_type_size(dst->type); - ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_SUB, { + ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_SUB, { (uint32_t)ggml_nelements(src0), (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2],(uint32_t)src0->ne[3], (uint32_t)src0->nb[0] / src0_type_size, (uint32_t)src0->nb[1] / src0_type_size, (uint32_t)src0->nb[2] / src0_type_size, (uint32_t)src0->nb[3] / src0_type_size, (uint32_t)src1->ne[0], (uint32_t)src1->ne[1], (uint32_t)src1->ne[2],(uint32_t)src1->ne[3], (uint32_t)src1->nb[0] / src1_type_size, (uint32_t)src1->nb[1] / src1_type_size, (uint32_t)src1->nb[2] / src1_type_size, (uint32_t)src1->nb[3] / src1_type_size, @@ -8891,7 +9151,7 @@ static void ggml_vk_mul(ggml_backend_vk_context * ctx, vk_context& subctx, const const uint32_t src1_type_size = ggml_type_size(src1->type); const uint32_t dst_type_size = ggml_type_size(dst->type); - ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_MUL, { + ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_MUL, { (uint32_t)ggml_nelements(src0), (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2],(uint32_t)src0->ne[3], (uint32_t)src0->nb[0] / src0_type_size, (uint32_t)src0->nb[1] / src0_type_size, (uint32_t)src0->nb[2] / src0_type_size, (uint32_t)src0->nb[3] / src0_type_size, (uint32_t)src1->ne[0], (uint32_t)src1->ne[1], (uint32_t)src1->ne[2],(uint32_t)src1->ne[3], (uint32_t)src1->nb[0] / src1_type_size, (uint32_t)src1->nb[1] / src1_type_size, (uint32_t)src1->nb[2] / src1_type_size, (uint32_t)src1->nb[3] / src1_type_size, @@ -8906,7 +9166,7 @@ static void ggml_vk_div(ggml_backend_vk_context * ctx, vk_context& subctx, const const uint32_t src1_type_size = ggml_type_size(src1->type); const uint32_t dst_type_size = ggml_type_size(dst->type); - ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_DIV, { + ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_DIV, { (uint32_t)ggml_nelements(src0), (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2],(uint32_t)src0->ne[3], (uint32_t)src0->nb[0] / src0_type_size, (uint32_t)src0->nb[1] / src0_type_size, (uint32_t)src0->nb[2] / src0_type_size, (uint32_t)src0->nb[3] / src0_type_size, (uint32_t)src1->ne[0], (uint32_t)src1->ne[1], (uint32_t)src1->ne[2],(uint32_t)src1->ne[3], (uint32_t)src1->nb[0] / src1_type_size, (uint32_t)src1->nb[1] / src1_type_size, (uint32_t)src1->nb[2] / src1_type_size, (uint32_t)src1->nb[3] / src1_type_size, @@ -8921,7 +9181,7 @@ static void ggml_vk_add_id(ggml_backend_vk_context * ctx, vk_context& subctx, co const uint32_t src1_type_size = ggml_type_size(src1->type); const uint32_t src2_type_size = ggml_type_size(src2->type); - ggml_vk_op_f32(ctx, subctx, src0, src1, src2, dst, GGML_OP_ADD_ID, { + ggml_vk_op_f32(ctx, subctx, src0, src1, src2, nullptr, dst, GGML_OP_ADD_ID, { (uint32_t)dst->ne[0], (uint32_t)dst->ne[1], (uint32_t)src0->nb[1] / src0_type_size, @@ -9154,7 +9414,7 @@ static void ggml_vk_ssm_conv(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0 = dst->src[0]; const ggml_tensor * src1 = dst->src[1]; - ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_SSM_CONV, { + ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_SSM_CONV, { (uint32_t)src0->nb[1], (uint32_t)src0->nb[2], (uint32_t)src1->nb[1], (uint32_t)dst->nb[0], (uint32_t)dst->nb[1], (uint32_t)dst->nb[2], @@ -9272,7 +9532,7 @@ static void ggml_vk_opt_step_adamw(ggml_backend_vk_context * ctx, vk_context& su static void ggml_vk_opt_step_sgd(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst, bool dryrun = false) { const size_t n = ggml_nelements(dst->src[0]); - ggml_vk_op_f32(ctx, subctx, src0, src1, src2, dst, GGML_OP_OPT_STEP_SGD, { (uint32_t)n, 0, 0.0f, 0.0f }, dryrun); + ggml_vk_op_f32(ctx, subctx, src0, src1, src2, nullptr, dst, GGML_OP_OPT_STEP_SGD, { (uint32_t)n, 0, 0.0f, 0.0f }, dryrun); } static void ggml_vk_concat(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, bool dryrun = false) { @@ -9282,7 +9542,7 @@ static void ggml_vk_concat(ggml_backend_vk_context * ctx, vk_context& subctx, co const uint32_t src1_type_size = ggml_type_size(src1->type); const uint32_t dst_type_size = ggml_type_size(dst->type); - ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_CONCAT, { + ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_CONCAT, { (uint32_t)ggml_nelements(dst), (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2],(uint32_t)src0->ne[3], (uint32_t)src0->nb[0] / src0_type_size, (uint32_t)src0->nb[1] / src0_type_size, (uint32_t)src0->nb[2] / src0_type_size, (uint32_t)src0->nb[3] / src0_type_size, (uint32_t)src1->ne[0], (uint32_t)src1->ne[1], (uint32_t)src1->ne[2],(uint32_t)src1->ne[3], (uint32_t)src1->nb[0] / src1_type_size, (uint32_t)src1->nb[1] / src1_type_size, (uint32_t)src1->nb[2] / src1_type_size, (uint32_t)src1->nb[3] / src1_type_size, @@ -9306,7 +9566,7 @@ static void ggml_vk_upscale(ggml_backend_vk_context * ctx, vk_context& subctx, c sf1 = (float)(dst->ne[1] - 1) / (src0->ne[1] - 1); } - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_UPSCALE, { + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_UPSCALE, { (uint32_t)ggml_nelements(dst), 0, 0, (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->nb[0] / src0_type_size, (uint32_t)src0->nb[1] / src0_type_size, (uint32_t)src0->nb[2] / src0_type_size, (uint32_t)src0->nb[3] / src0_type_size, @@ -9320,23 +9580,23 @@ static void ggml_vk_scale(ggml_backend_vk_context * ctx, vk_context& subctx, con p.param1 = ggml_get_op_params_f32(dst, 0); p.param2 = ggml_get_op_params_f32(dst, 1); - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_SCALE, std::move(p), dryrun); + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_SCALE, std::move(p), dryrun); } static void ggml_vk_sqr(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_SQR, vk_op_unary_push_constants_init(src0, dst), dryrun); + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_SQR, vk_op_unary_push_constants_init(src0, dst), dryrun); } static void ggml_vk_sqrt(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_SQRT, vk_op_unary_push_constants_init(src0, dst), dryrun); + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_SQRT, vk_op_unary_push_constants_init(src0, dst), dryrun); } static void ggml_vk_sin(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_SIN, vk_op_unary_push_constants_init(src0, dst), dryrun); + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_SIN, vk_op_unary_push_constants_init(src0, dst), dryrun); } static void ggml_vk_cos(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_COS, vk_op_unary_push_constants_init(src0, dst), dryrun); + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_COS, vk_op_unary_push_constants_init(src0, dst), dryrun); } static void ggml_vk_clamp(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { @@ -9344,12 +9604,12 @@ static void ggml_vk_clamp(ggml_backend_vk_context * ctx, vk_context& subctx, con p.param1 = ggml_get_op_params_f32(dst, 0); p.param2 = ggml_get_op_params_f32(dst, 1); - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_CLAMP, std::move(p), dryrun); + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_CLAMP, std::move(p), dryrun); } static void ggml_vk_pad(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { vk_op_pad_push_constants p = vk_op_pad_push_constants_init(src0, dst); - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_PAD, std::move(p), dryrun); + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_PAD, std::move(p), dryrun); } static void ggml_vk_roll(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { @@ -9364,17 +9624,17 @@ static void ggml_vk_roll(ggml_backend_vk_context * ctx, vk_context& subctx, cons memcpy(&p.param1, &s01_packed, sizeof(float)); memcpy(&p.param2, &s23_packed, sizeof(float)); - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_ROLL, std::move(p), dryrun); + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_ROLL, std::move(p), dryrun); } static void ggml_vk_repeat(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { vk_op_unary_push_constants p = vk_op_unary_push_constants_init(src0, dst, ggml_nelements(dst)); - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_REPEAT, std::move(p), dryrun); + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_REPEAT, std::move(p), dryrun); } static void ggml_vk_repeat_back(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { vk_op_unary_push_constants p = vk_op_unary_push_constants_init(src0, dst, ggml_nelements(dst)); - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_REPEAT_BACK, std::move(p), dryrun); + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_REPEAT_BACK, std::move(p), dryrun); } static void ggml_vk_cpy(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { @@ -9390,7 +9650,7 @@ static void ggml_vk_cpy(ggml_backend_vk_context * ctx, vk_context& subctx, const } vk_op_unary_push_constants p = vk_op_unary_push_constants_init(src0, dst, ne); - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_CPY, std::move(p), dryrun); + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_CPY, std::move(p), dryrun); } static void ggml_vk_set_rows(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, bool dryrun = false) { @@ -9405,7 +9665,7 @@ static void ggml_vk_set_rows(ggml_backend_vk_context * ctx, vk_context& subctx, return; } - ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_SET_ROWS, { + ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_SET_ROWS, { (uint32_t)ggml_nelements(src0), (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2],(uint32_t)src0->ne[3], (uint32_t)src0->nb[0] / src0_type_size, (uint32_t)src0->nb[1] / src0_type_size, (uint32_t)src0->nb[2] / src0_type_size, (uint32_t)src0->nb[3] / src0_type_size, (uint32_t)src1->ne[0], (uint32_t)src1->ne[1], (uint32_t)src1->ne[2],(uint32_t)src1->ne[3], (uint32_t)src1->nb[0] / src1_type_size, (uint32_t)src1->nb[1] / src1_type_size, (uint32_t)src1->nb[2] / src1_type_size, (uint32_t)src1->nb[3] / src1_type_size, @@ -9416,13 +9676,13 @@ static void ggml_vk_set_rows(ggml_backend_vk_context * ctx, vk_context& subctx, } static void ggml_vk_silu_back(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, bool dryrun = false) { - ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_SILU_BACK, { (uint32_t)ggml_nelements(src0), 0, 0.0f, 0.0f }, dryrun); + ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_SILU_BACK, { (uint32_t)ggml_nelements(src0), 0, 0.0f, 0.0f }, dryrun); } static void ggml_vk_norm(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { float * op_params = (float *)dst->op_params; - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_NORM, { (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], op_params[0], 0.0f }, dryrun); + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_NORM, { (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], op_params[0], 0.0f }, dryrun); } static void ggml_vk_group_norm(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { @@ -9433,7 +9693,7 @@ static void ggml_vk_group_norm(ggml_backend_vk_context * ctx, vk_context& subctx const float eps = float_op_params[1]; const uint32_t group_size = src0->ne[0] * src0->ne[1] * ((src0->ne[2] + num_groups - 1) / num_groups); - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_GROUP_NORM, { group_size, 0, eps, 0.0f }, dryrun); + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_GROUP_NORM, { group_size, 0, eps, 0.0f }, dryrun); } static uint32_t ggml_vk_rms_num_partials(ggml_backend_vk_context * ctx, const ggml_tensor *node) { @@ -9456,7 +9716,7 @@ static void ggml_vk_rms_norm(ggml_backend_vk_context * ctx, vk_context& subctx, uint32_t param3 = ctx->do_add_rms_partials ? ggml_vk_rms_num_partials(ctx, dst) : 0; - ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_RMS_NORM, { + ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_RMS_NORM, { (uint32_t)ggml_nelements(src0), (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2],(uint32_t)src0->ne[3], (uint32_t)src0->nb[0] / src0_type_size, (uint32_t)src0->nb[1] / src0_type_size, (uint32_t)src0->nb[2] / src0_type_size, (uint32_t)src0->nb[3] / src0_type_size, (uint32_t)src1->ne[0], (uint32_t)src1->ne[1], (uint32_t)src1->ne[2],(uint32_t)src1->ne[3], (uint32_t)src1->nb[0] / src1_type_size, (uint32_t)src1->nb[1] / src1_type_size, (uint32_t)src1->nb[2] / src1_type_size, (uint32_t)src1->nb[3] / src1_type_size, @@ -9473,16 +9733,16 @@ static void ggml_vk_rms_norm(ggml_backend_vk_context * ctx, vk_context& subctx, static void ggml_vk_rms_norm_back(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, bool dryrun = false) { float * op_params = (float *)dst->op_params; - ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_RMS_NORM_BACK, { (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], op_params[0], 0.0f }, dryrun); + ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_RMS_NORM_BACK, { (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], op_params[0], 0.0f }, dryrun); } static void ggml_vk_l2_norm(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { float * op_params = (float *)dst->op_params; - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_L2_NORM, { (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], op_params[0], 0.0f }, dryrun); + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_L2_NORM, { (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], op_params[0], 0.0f }, dryrun); } static void ggml_vk_unary(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_UNARY, { (uint32_t)ggml_nelements(src0), 0, 0.0f, 0.0f }, dryrun); + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_UNARY, { (uint32_t)ggml_nelements(src0), 0, 0.0f, 0.0f }, dryrun); } static void ggml_vk_glu(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, bool dryrun = false) { @@ -9505,7 +9765,7 @@ static void ggml_vk_glu(ggml_backend_vk_context * ctx, vk_context& subctx, const const uint32_t mode = split ? 2 : (swapped ? 1 : 0); - ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_GLU, + ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_GLU, { (uint32_t)ggml_nelements(dst), (uint32_t)src0->ne[0], @@ -9518,7 +9778,7 @@ static void ggml_vk_glu(ggml_backend_vk_context * ctx, vk_context& subctx, const static void ggml_vk_diag_mask_inf(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { int32_t * op_params = (int32_t *)dst->op_params; - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_DIAG_MASK_INF, { (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], op_params[0] }, dryrun); + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_DIAG_MASK_INF, { (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], op_params[0] }, dryrun); } static void ggml_vk_soft_max(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst, bool dryrun = false) { @@ -9543,7 +9803,7 @@ static void ggml_vk_soft_max(ggml_backend_vk_context * ctx, vk_context& subctx, const float m0 = powf(2.0f, -(max_bias ) / n_head_log2); const float m1 = powf(2.0f, -(max_bias / 2.0f) / n_head_log2); - ggml_vk_op_f32(ctx, subctx, src0, src1, src2, dst, GGML_OP_SOFT_MAX, { + ggml_vk_op_f32(ctx, subctx, src0, src1, src2, nullptr, dst, GGML_OP_SOFT_MAX, { ncols, src1 != nullptr ? nrows_y : (uint32_t)0, (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], (uint32_t)src0->ne[2], @@ -9559,15 +9819,17 @@ static void ggml_vk_soft_max(ggml_backend_vk_context * ctx, vk_context& subctx, static void ggml_vk_soft_max_back(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, bool dryrun = false) { float * op_params = (float *)dst->op_params; - ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_SOFT_MAX_BACK, { (uint32_t)src0->ne[0], (uint32_t)ggml_nrows(src0), op_params[0], op_params[1] }, dryrun); + ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_SOFT_MAX_BACK, { (uint32_t)src0->ne[0], (uint32_t)ggml_nrows(src0), op_params[0], op_params[1] }, dryrun); } static void ggml_vk_topk_moe(ggml_backend_vk_context * ctx, vk_context& subctx, ggml_cgraph * cgraph, int node_idx, bool dryrun = false) { - bool with_norm = ctx->num_additional_fused_ops == topk_moe_norm.size() - 1; + topk_moe_mode mode = ggml_vk_num_additional_ops_to_topk_moe_mode(ctx->num_additional_fused_ops); ggml_tensor * logits = cgraph->nodes[node_idx + 0]->src[0]; - ggml_tensor * weights = with_norm ? cgraph->nodes[node_idx + 8] : cgraph->nodes[node_idx + 4]; - ggml_tensor * ids = cgraph->nodes[node_idx + 3]; + ggml_tensor * weights = (mode == TOPK_MOE_EARLY_SOFTMAX_NORM) ? cgraph->nodes[node_idx + 9] : + (mode == TOPK_MOE_EARLY_SOFTMAX) ? cgraph->nodes[node_idx + 4] : + cgraph->nodes[node_idx + 5]; + ggml_tensor * ids = (mode == TOPK_MOE_LATE_SOFTMAX) ? cgraph->nodes[node_idx + 1] : cgraph->nodes[node_idx + 3]; GGML_ASSERT(logits->type == GGML_TYPE_F32); GGML_ASSERT(weights->type == GGML_TYPE_F32); @@ -9626,9 +9888,14 @@ static void ggml_vk_topk_moe(ggml_backend_vk_context * ctx, vk_context& subctx, GGML_ASSERT(d_ids != nullptr); } - vk_op_topk_moe_push_constants pc; + vk_op_topk_moe_push_constants pc {}; pc.n_rows = n_rows; pc.n_expert_used = n_expert_used; + if (mode == TOPK_MOE_EARLY_SOFTMAX_NORM) { + ggml_tensor * clamp = cgraph->nodes[node_idx + 7]; + pc.clamp_min = ggml_get_op_params_f32(clamp, 0); + pc.clamp_max = ggml_get_op_params_f32(clamp, 1); + } GGML_ASSERT(n_expert_used <= n_experts); @@ -9643,7 +9910,12 @@ static void ggml_vk_topk_moe(ggml_backend_vk_context * ctx, vk_context& subctx, }, pc, elements); } -static void ggml_vk_rope(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, const ggml_tensor * src2, ggml_tensor * dst, bool backprop, bool dryrun = false) { +static void ggml_vk_rope(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_cgraph * cgraph, int node_idx, bool backprop, bool dryrun = false) { + ggml_tensor * dst = cgraph->nodes[node_idx]; + const ggml_tensor * src0 = dst->src[0]; + const ggml_tensor * src1 = dst->src[1]; + const ggml_tensor * src2 = dst->src[2]; + const ggml_tensor * src3 = nullptr; const int n_dims = ((int32_t *) dst->op_params)[1]; const int mode = ((int32_t *) dst->op_params)[2]; // const int n_ctx = ((int32_t *) dst->op_params)[3]; @@ -9667,11 +9939,20 @@ static void ggml_vk_rope(ggml_backend_vk_context * ctx, vk_context& subctx, cons uint32_t s1 = src0->nb[1] / ggml_type_size(src0->type); uint32_t s2 = src0->nb[2] / ggml_type_size(src0->type); - ggml_vk_op_f32(ctx, subctx, src0, src1, src2, dst, GGML_OP_ROPE, { + uint32_t set_rows_stride = 0; + // Fused rope + view + set_rows passes the set_rows destination stride in set_rows_stride + // and overrides the dst and sets src3=row_indices + if (ctx->num_additional_fused_ops > 0) { + set_rows_stride = cgraph->nodes[node_idx + 2]->nb[1] / ggml_type_size(cgraph->nodes[node_idx + 2]->type); + src3 = cgraph->nodes[node_idx + 2]->src[1]; + dst = cgraph->nodes[node_idx + 2]; + } + + ggml_vk_op_f32(ctx, subctx, src0, src1, src2, src3, dst, GGML_OP_ROPE, { (uint32_t)src0->ne[0], (uint32_t)n_dims, freq_scale, (uint32_t)src0->ne[1], freq_base, ext_factor, attn_factor, {corr_dims[0], corr_dims[1]}, theta_scale, src2 != nullptr, (uint32_t)src0->ne[2], s1, s2, - { sections[0], sections[1], sections[2], sections[3] }, backprop + { sections[0], sections[1], sections[2], sections[3] }, backprop, set_rows_stride, }, dryrun); } @@ -9679,35 +9960,37 @@ static void ggml_vk_argsort(ggml_backend_vk_context * ctx, vk_context& subctx, c int32_t * op_params = (int32_t *)dst->op_params; uint32_t ncols = src0->ne[0]; + uint32_t nrows = ggml_nrows(src0); - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_ARGSORT, { + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_ARGSORT, { ncols, + nrows, op_params[0], }, dryrun); } static void ggml_vk_sum(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { vk_op_sum_rows_push_constants p = vk_op_sum_rows_push_constants_init(src0, dst, ggml_nelements(src0)); - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_SUM, p, dryrun); + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_SUM, p, dryrun); } static void ggml_vk_sum_rows(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { vk_op_sum_rows_push_constants p = vk_op_sum_rows_push_constants_init(src0, dst, src0->ne[0]); - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_SUM_ROWS, p, dryrun); + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_SUM_ROWS, p, dryrun); } static void ggml_vk_mean(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { vk_op_sum_rows_push_constants p = vk_op_sum_rows_push_constants_init(src0, dst, src0->ne[0]); p.weight = 1.0f / (float)src0->ne[0]; - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_MEAN, p, dryrun); + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_MEAN, p, dryrun); } static void ggml_vk_argmax(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_ARGMAX, { (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], 0.0f, 0.0f }, dryrun); + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_ARGMAX, { (uint32_t)src0->ne[0], (uint32_t)src0->ne[1], 0.0f, 0.0f }, dryrun); } static void ggml_vk_count_equal(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, bool dryrun = false) { - ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_COUNT_EQUAL, { (uint32_t)ggml_nelements(src0), 0, 0.0f, 0.0f }, dryrun); + ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_COUNT_EQUAL, { (uint32_t)ggml_nelements(src0), 0, 0.0f, 0.0f }, dryrun); } static void ggml_vk_im2col(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, bool dryrun = false) { @@ -9740,7 +10023,7 @@ static void ggml_vk_im2col(ggml_backend_vk_context * ctx, vk_context& subctx, co const vk::DeviceAddress dst_addr = d_buf->bda_addr + vk_tensor_offset(dst) + dst->view_offs; - ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_IM2COL, { + ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_IM2COL, { dst_addr, batch_offset, offset_delta, IC, IW, IH, OW, OH, KW, KH, @@ -9813,7 +10096,7 @@ static void ggml_vk_im2col_3d(ggml_backend_vk_context * ctx, vk_context& subctx, pc.OH_OW_IC_KD_KH_KW = OH*OW*IC*KD*KH*KW; pc.OW_IC_KD_KH_KW = OW*IC*KD*KH*KW; - ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_IM2COL_3D, std::move(pc), dryrun); + ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_IM2COL_3D, std::move(pc), dryrun); } static void ggml_vk_timestep_embedding(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { @@ -9821,7 +10104,7 @@ static void ggml_vk_timestep_embedding(ggml_backend_vk_context * ctx, vk_context const uint32_t max_period = dst->op_params[1]; const uint32_t nb1 = dst->nb[1] / ggml_type_size(dst->type); - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_TIMESTEP_EMBEDDING, { + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_TIMESTEP_EMBEDDING, { nb1, dim, max_period, }, dryrun); } @@ -9854,7 +10137,7 @@ static void ggml_vk_conv_transpose_1d(ggml_backend_vk_context * ctx, vk_context& p.nb1 = static_cast(nb1 / nb0); p.s0 = static_cast(s0); - ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_CONV_TRANSPOSE_1D, std::move(p), dryrun); + ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_CONV_TRANSPOSE_1D, std::move(p), dryrun); } static void ggml_vk_pool_2d(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { @@ -9877,7 +10160,7 @@ static void ggml_vk_pool_2d(ggml_backend_vk_context * ctx, vk_context& subctx, c const uint32_t parallel_elements = N * OC * OH * OW; - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_POOL_2D, { + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_POOL_2D, { IW, IH, OW, OH, OC, parallel_elements, op, @@ -9931,7 +10214,7 @@ static void ggml_vk_conv_2d(ggml_backend_vk_context * ctx, vk_context & subctx, GGML_ASSERT(ne03 == ne2); GGML_ASSERT(ne02 == ne12); - ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_CONV_2D, std::move(p), dryrun); + ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_CONV_2D, std::move(p), dryrun); } static void ggml_vk_conv_transpose_2d(ggml_backend_vk_context * ctx, vk_context & subctx, const ggml_tensor * src0, @@ -9980,7 +10263,7 @@ static void ggml_vk_conv_transpose_2d(ggml_backend_vk_context * ctx, vk_context GGML_ASSERT(ne02 == ne2); GGML_ASSERT(ne03 == ne12); - ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_CONV_TRANSPOSE_2D, std::move(p), dryrun); + ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_CONV_TRANSPOSE_2D, std::move(p), dryrun); } static void ggml_vk_conv_2d_dw(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, const ggml_tensor * src1, ggml_tensor * dst, bool dryrun = false) { @@ -10004,12 +10287,12 @@ static void ggml_vk_conv_2d_dw(ggml_backend_vk_context * ctx, vk_context& subctx GGML_ASSERT(src0->ne[3] == p.channels); GGML_ASSERT(src1->ne[3] == p.batches); - ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, dst, GGML_OP_CONV_2D_DW, std::move(p), dryrun); + ggml_vk_op_f32(ctx, subctx, src0, src1, nullptr, nullptr, dst, GGML_OP_CONV_2D_DW, std::move(p), dryrun); } static void ggml_vk_leaky_relu(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst, bool dryrun = false) { const float * op_params = (const float *)dst->op_params; - ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, dst, GGML_OP_LEAKY_RELU, { (uint32_t)ggml_nelements(src0), 0, op_params[0], 0.0f }, dryrun); + ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_LEAKY_RELU, { (uint32_t)ggml_nelements(src0), 0, op_params[0], 0.0f }, dryrun); } #ifdef GGML_VULKAN_RUN_TESTS @@ -11135,7 +11418,6 @@ static bool ggml_vk_build_graph(ggml_backend_vk_context * ctx, ggml_cgraph * cgr case GGML_OP_DIAG_MASK_INF: case GGML_OP_SOFT_MAX: case GGML_OP_SOFT_MAX_BACK: - case GGML_OP_ROPE: case GGML_OP_ROPE_BACK: case GGML_OP_ARGSORT: case GGML_OP_SUM: @@ -11209,9 +11491,12 @@ static bool ggml_vk_build_graph(ggml_backend_vk_context * ctx, ggml_cgraph * cgr // nodes require synchronization. for (int32_t i = 0; i < ctx->num_additional_fused_ops + 1 && !need_sync; ++i) { const ggml_tensor *cur_node = cgraph->nodes[node_idx + i]; - if (overlaps_unsynced(cur_node, ctx->unsynced_nodes_read) || overlaps_unsynced(cur_node, ctx->unsynced_nodes_written)) { - need_sync = true; - break; + // If the node actually writes to memory, then check if it needs to sync + if (ctx->fused_ops_write_mask & (1 << i)) { + if (overlaps_unsynced(cur_node, ctx->unsynced_nodes_read) || overlaps_unsynced(cur_node, ctx->unsynced_nodes_written)) { + need_sync = true; + break; + } } for (uint32_t j = 0; j < GGML_MAX_SRC; ++j) { if (!cur_node->src[j]) { @@ -11223,7 +11508,13 @@ static bool ggml_vk_build_graph(ggml_backend_vk_context * ctx, ggml_cgraph * cgr } } } + +#define ENABLE_SYNC_LOGGING 0 + if (need_sync) { +#if ENABLE_SYNC_LOGGING + std::cerr << "sync" << std::endl; +#endif ctx->unsynced_nodes_written.clear(); ctx->unsynced_nodes_read.clear(); ggml_vk_sync_buffers(ctx, compute_ctx); @@ -11232,7 +11523,9 @@ static bool ggml_vk_build_graph(ggml_backend_vk_context * ctx, ggml_cgraph * cgr for (int32_t i = 0; i < ctx->num_additional_fused_ops + 1; ++i) { const ggml_tensor *cur_node = cgraph->nodes[node_idx + i]; // Multiple outputs could be written, e.g. in topk_moe. Add them all to the list. - ctx->unsynced_nodes_written.push_back(cur_node); + if (ctx->fused_ops_write_mask & (1 << i)) { + ctx->unsynced_nodes_written.push_back(cur_node); + } for (uint32_t j = 0; j < GGML_MAX_SRC; ++j) { if (!cur_node->src[j]) { continue; @@ -11241,6 +11534,18 @@ static bool ggml_vk_build_graph(ggml_backend_vk_context * ctx, ggml_cgraph * cgr } } } +#if ENABLE_SYNC_LOGGING + if (!dryrun) { + for (int i = 0; i < ctx->num_additional_fused_ops + 1; ++i) { + auto *n = cgraph->nodes[node_idx + i]; + std::cerr << node_idx + i << " " << ggml_op_name(n->op) << " " << n->name; + if (n->op == GGML_OP_GLU) { + std::cerr << " " << ggml_glu_op_name(ggml_get_glu_op(n)) << " " << (n->src[1] ? "split" : "single") << " "; + } + std::cerr << std::endl; + } + } +#endif switch (node->op) { case GGML_OP_REPEAT: @@ -11411,15 +11716,19 @@ static bool ggml_vk_build_graph(ggml_backend_vk_context * ctx, ggml_cgraph * cgr break; case GGML_OP_ROPE: - ggml_vk_rope(ctx, compute_ctx, src0, src1, src2, node, false, dryrun); + ggml_vk_rope(ctx, compute_ctx, cgraph, node_idx, false, dryrun); break; case GGML_OP_ROPE_BACK: - ggml_vk_rope(ctx, compute_ctx, src0, src1, src2, node, true, dryrun); + ggml_vk_rope(ctx, compute_ctx, cgraph, node_idx, true, dryrun); break; case GGML_OP_ARGSORT: - ggml_vk_argsort(ctx, compute_ctx, src0, node, dryrun); + if (ctx->num_additional_fused_ops) { + ggml_vk_topk_moe(ctx, compute_ctx, cgraph, node_idx, dryrun); + } else { + ggml_vk_argsort(ctx, compute_ctx, src0, node, dryrun); + } break; case GGML_OP_SUM: @@ -12217,31 +12526,28 @@ static bool ggml_vk_can_fuse(const struct ggml_cgraph * cgraph, int node_idx, st } static bool ggml_vk_can_fuse_topk_moe(ggml_backend_vk_context * ctx, const struct ggml_cgraph * cgraph, - int node_idx, bool with_norm) { + int node_idx, topk_moe_mode mode) { - if (with_norm) { - if (node_idx + (int)topk_moe_norm.size() > cgraph->n_nodes) { - return false; - } - for (size_t i = 0; i < topk_moe_norm.size(); ++i) { - if (cgraph->nodes[node_idx + i]->op != topk_moe_norm[i]) { - return false; - } - } - } else { - if (node_idx + (int)topk_moe.size() > cgraph->n_nodes) { - return false; - } - for (size_t i = 0; i < topk_moe.size(); ++i) { - if (cgraph->nodes[node_idx + i]->op != topk_moe[i]) { - return false; - } - } + const ggml_tensor * softmax; + const ggml_tensor * weights; + + switch (mode) { + case TOPK_MOE_EARLY_SOFTMAX_NORM: + softmax = cgraph->nodes[node_idx + 0]; + weights = cgraph->nodes[node_idx + 9]; + break; + case TOPK_MOE_EARLY_SOFTMAX: + softmax = cgraph->nodes[node_idx + 0]; + weights = cgraph->nodes[node_idx + 4]; + break; + case TOPK_MOE_LATE_SOFTMAX: + softmax = cgraph->nodes[node_idx + 4]; + weights = cgraph->nodes[node_idx + 5]; + break; + default: + return false; } - const ggml_tensor * softmax = cgraph->nodes[node_idx + 0]; - const ggml_tensor * weights = with_norm ? cgraph->nodes[node_idx + 8] : cgraph->nodes[node_idx + 4]; - const float * op_params = (const float *)softmax->op_params; float scale = op_params[0]; @@ -12266,60 +12572,6 @@ static bool ggml_vk_can_fuse_topk_moe(ggml_backend_vk_context * ctx, const struc return false; } - // Check that the nodes don't have any unexpected uses - const ggml_tensor * reshape1 = cgraph->nodes[node_idx + 1]; - const ggml_tensor * argsort = cgraph->nodes[node_idx + 2]; - const ggml_tensor * view = cgraph->nodes[node_idx + 3]; - const ggml_tensor * get_rows = cgraph->nodes[node_idx + 4]; - const ggml_tensor * reshape5 = with_norm ? cgraph->nodes[node_idx + 5] : nullptr; - const ggml_tensor * sum_rows = with_norm ? cgraph->nodes[node_idx + 6] : nullptr; - const ggml_tensor * div = with_norm ? cgraph->nodes[node_idx + 7] : nullptr; - const ggml_tensor * reshape8 = with_norm ? cgraph->nodes[node_idx + 8] : nullptr; - - // softmax is used by reshape and argsort - if (ggml_node_get_use_count(cgraph, node_idx) != 2 || - reshape1->src[0] != softmax || - argsort->src[0] != softmax) { - return false; - } - // reshape is used by get_rows - if (ggml_node_get_use_count(cgraph, node_idx + 1) != 1 || - get_rows->src[0] != reshape1) { - return false; - } - // argsort is used by view - if (ggml_node_get_use_count(cgraph, node_idx + 2) != 1 || - view->src[0] != argsort) { - return false; - } - // view is written (via argsort), we can skip checking it - - if (with_norm) { - // get_rows is used by reshape - if (ggml_node_get_use_count(cgraph, node_idx + 4) != 1 || - reshape5->src[0] != get_rows) { - return false; - } - - // reshape is used by sum_rows and div - if (ggml_node_get_use_count(cgraph, node_idx + 5) != 2 || - sum_rows->src[0] != reshape5 || - div->src[0] != reshape5) { - return false; - } - - // sum_rows is used by div - if (ggml_node_get_use_count(cgraph, node_idx + 6) != 1 || - div->src[1] != sum_rows) { - return false; - } - - // div/reshape are written - if (reshape8->src[0] != div) { - return false; - } - } - if (!ctx->device->subgroup_arithmetic || !ctx->device->subgroup_shuffle || !ctx->device->subgroup_require_full_support || @@ -12330,6 +12582,41 @@ static bool ggml_vk_can_fuse_topk_moe(ggml_backend_vk_context * ctx, const struc return true; } +static bool ggml_vk_can_fuse_rope_set_rows(ggml_backend_vk_context * ctx, const struct ggml_cgraph * cgraph, + int node_idx) { + GGML_UNUSED(ctx); + const ggml_tensor *rope = cgraph->nodes[node_idx + 0]; + const ggml_tensor *view = cgraph->nodes[node_idx + 1]; + const ggml_tensor *set_rows = cgraph->nodes[node_idx + 2]; + + // ne3 not tested + if (rope->src[0]->ne[3] != 1) { + return false; + } + + if (set_rows->type != GGML_TYPE_F32 && set_rows->type != GGML_TYPE_F16) { + return false; + } + + if (set_rows->src[1]->type != GGML_TYPE_I64) { + return false; + } + + // The view should flatten two dims of rope into one dim + if (!ggml_is_contiguous(view) || + view->ne[0] != rope->ne[0] * rope->ne[1]) { + return false; + } + + // Only norm/neox shaders have the fusion code + const int mode = ((const int32_t *) rope->op_params)[2]; + if (mode != GGML_ROPE_TYPE_NORMAL && mode != GGML_ROPE_TYPE_NEOX) { + return false; + } + + return true; +} + static uint32_t ggml_vk_fuse_multi_add(ggml_backend_vk_context * ctx, const struct ggml_cgraph * cgraph, int node_idx) { const ggml_tensor *first_node = cgraph->nodes[node_idx]; @@ -12405,10 +12692,22 @@ static ggml_status ggml_backend_vk_graph_compute(ggml_backend_t backend, ggml_cg ctx->num_additional_fused_ops = num_adds - 1; } else if (ggml_vk_can_fuse(cgraph, i, { GGML_OP_RMS_NORM, GGML_OP_MUL })) { ctx->num_additional_fused_ops = 1; - } else if (ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, true)) { - ctx->num_additional_fused_ops = topk_moe_norm.size() - 1; - } else if (ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, false)) { - ctx->num_additional_fused_ops = topk_moe.size() - 1; + } else if (ggml_can_fuse_subgraph(cgraph, i, { GGML_OP_ROPE, GGML_OP_VIEW, GGML_OP_SET_ROWS }, { i + 2 }) && + ggml_check_edges(cgraph, i, rope_view_set_rows_edges) && + ggml_vk_can_fuse_rope_set_rows(ctx, cgraph, i)) { + ctx->num_additional_fused_ops = 2; + } else if (ggml_can_fuse_subgraph(cgraph, i, topk_moe_early_softmax_norm, { i + 3, i + 9 }) && + ggml_check_edges(cgraph, i, topk_moe_early_softmax_norm_edges) && + ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, TOPK_MOE_EARLY_SOFTMAX_NORM)) { + ctx->num_additional_fused_ops = topk_moe_early_softmax_norm.size() - 1; + } else if (ggml_can_fuse_subgraph(cgraph, i, topk_moe_early_softmax, { i + 3, i + 4 }) && + ggml_check_edges(cgraph, i, topk_moe_early_softmax_edges) && + ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, TOPK_MOE_EARLY_SOFTMAX)) { + ctx->num_additional_fused_ops = topk_moe_early_softmax.size() - 1; + } else if (ggml_can_fuse_subgraph(cgraph, i, topk_moe_late_softmax, { i + 1, i + 5 }) && + ggml_check_edges(cgraph, i, topk_moe_late_softmax_edges) && + ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, TOPK_MOE_LATE_SOFTMAX)) { + ctx->num_additional_fused_ops = topk_moe_late_softmax.size() - 1; } } ggml_vk_build_graph(ctx, cgraph, i, nullptr, 0, true, false, false, false); @@ -12506,12 +12805,31 @@ static ggml_status ggml_backend_vk_graph_compute(ggml_backend_t backend, ggml_cg ctx->num_additional_fused_ops = num_adds - 1; } else if (ggml_vk_can_fuse(cgraph, i, { GGML_OP_RMS_NORM, GGML_OP_MUL })) { ctx->num_additional_fused_ops = 1; - } else if (ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, true)) { - ctx->num_additional_fused_ops = topk_moe_norm.size() - 1; - } else if (ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, false)) { - ctx->num_additional_fused_ops = topk_moe.size() - 1; + } else if (ggml_can_fuse_subgraph(cgraph, i, { GGML_OP_ROPE, GGML_OP_VIEW, GGML_OP_SET_ROWS }, { i + 2 }) && + ggml_check_edges(cgraph, i, rope_view_set_rows_edges) && + ggml_vk_can_fuse_rope_set_rows(ctx, cgraph, i)) { + ctx->num_additional_fused_ops = 2; + } else if (ggml_can_fuse_subgraph(cgraph, i, topk_moe_early_softmax_norm, { i + 3, i + 9 }) && + ggml_check_edges(cgraph, i, topk_moe_early_softmax_norm_edges) && + ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, TOPK_MOE_EARLY_SOFTMAX_NORM)) { + ctx->num_additional_fused_ops = topk_moe_early_softmax_norm.size() - 1; + // view of argsort writes to memory + ctx->fused_ops_write_mask |= 1 << 3; + } else if (ggml_can_fuse_subgraph(cgraph, i, topk_moe_early_softmax, { i + 3, i + 4 }) && + ggml_check_edges(cgraph, i, topk_moe_early_softmax_edges) && + ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, TOPK_MOE_EARLY_SOFTMAX)) { + ctx->num_additional_fused_ops = topk_moe_early_softmax.size() - 1; + // view of argsort writes to memory + ctx->fused_ops_write_mask |= 1 << 3; + } else if (ggml_can_fuse_subgraph(cgraph, i, topk_moe_late_softmax, { i + 1, i + 5 }) && + ggml_check_edges(cgraph, i, topk_moe_late_softmax_edges) && + ggml_vk_can_fuse_topk_moe(ctx, cgraph, i, TOPK_MOE_LATE_SOFTMAX)) { + ctx->num_additional_fused_ops = topk_moe_late_softmax.size() - 1; + // view of argsort writes to memory + ctx->fused_ops_write_mask |= 1 << 1; } } + ctx->fused_ops_write_mask |= 1 << ctx->num_additional_fused_ops; // Signal the almost_ready fence when the graph is mostly complete (< 20% remaining) bool almost_ready = (cgraph->n_nodes - i) < cgraph->n_nodes / 5; @@ -12557,6 +12875,7 @@ static ggml_status ggml_backend_vk_graph_compute(ggml_backend_t backend, ggml_cg } i += ctx->num_additional_fused_ops; ctx->num_additional_fused_ops = 0; + ctx->fused_ops_write_mask = 0; } if (vk_perf_logger_enabled) { @@ -12642,25 +12961,44 @@ static void ggml_vk_graph_optimize(ggml_backend_t backend, struct ggml_cgraph * while (first_unused < graph->n_nodes) { std::vector current_set; - // Avoid reordering topk_moe_norm - if (first_unused + (int)topk_moe_norm.size() <= graph->n_nodes) { - bool is_topk_moe_norm = true; - for (size_t j = 0; j < topk_moe_norm.size(); ++j) { - if (graph->nodes[first_unused + j]->op != topk_moe_norm[j] || used[first_unused + j]) { - is_topk_moe_norm = false; + // Check for fusion patterns and avoid reordering them + auto const &match_pattern = [&](const std::initializer_list &pattern, int start) -> bool { + if (start + (int)pattern.size() <= graph->n_nodes) { + bool is_pattern = true; + for (size_t j = 0; j < pattern.size(); ++j) { + if (graph->nodes[start + j]->op != pattern.begin()[j] || used[start + j]) { + is_pattern = false; + } } + return is_pattern; } - if (is_topk_moe_norm) { - for (size_t j = 0; j < topk_moe_norm.size(); ++j) { + return false; + }; + + auto const &keep_pattern = [&](const std::initializer_list &pattern) -> bool { + if (match_pattern(pattern, first_unused)) { + for (size_t j = 0; j < pattern.size(); ++j) { new_order.push_back(graph->nodes[first_unused + j]); used[first_unused + j] = true; } while (first_unused < graph->n_nodes && used[first_unused]) { first_unused++; } - continue; + return true; } + return false; + }; + + if (keep_pattern(topk_moe_early_softmax_norm)) { + continue; } + if (keep_pattern(topk_moe_early_softmax)) { + continue; + } + if (keep_pattern(topk_moe_late_softmax)) { + continue; + } + // First, grab the next unused node. current_set.push_back(first_unused); @@ -12678,6 +13016,12 @@ static void ggml_vk_graph_optimize(ggml_backend_t backend, struct ggml_cgraph * if (is_empty(graph->nodes[j])) { continue; } + // Don't pull forward nodes from fusion patterns + if (match_pattern(topk_moe_early_softmax_norm, j) || + match_pattern(topk_moe_early_softmax, j) || + match_pattern(topk_moe_late_softmax, j)) { + continue; + } bool ok = true; for (int c = first_unused; c < j; ++c) { if (!used[c] && @@ -12689,6 +13033,32 @@ static void ggml_vk_graph_optimize(ggml_backend_t backend, struct ggml_cgraph * } if (ok) { current_set.push_back(j); + // Look for ROPE + VIEW + SET_ROWS and make them consecutive + if (graph->nodes[j]->op == GGML_OP_ROPE) { + int view_idx = -1; + int set_rows_idx = -1; + for (int k = j+1; k < std::min(j + 10, graph->n_nodes); ++k) { + if (view_idx == -1 && + graph->nodes[k]->op == GGML_OP_VIEW && + graph->nodes[k]->src[0] == graph->nodes[j]) { + view_idx = k; + continue; + } + if (view_idx != -1 && + set_rows_idx == -1 && + graph->nodes[k]->op == GGML_OP_SET_ROWS && + graph->nodes[k]->src[0] == graph->nodes[view_idx]) { + set_rows_idx = k; + break; + } + } + if (set_rows_idx != -1) { + current_set.push_back(view_idx); + current_set.push_back(set_rows_idx); + used[view_idx] = true; + used[set_rows_idx] = true; + } + } } } // Second pass grabs view nodes. diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/argsort.comp b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/argsort.comp index c81b8445..c4e68bc0 100644 --- a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/argsort.comp +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/argsort.comp @@ -14,6 +14,7 @@ layout (binding = 1) buffer D {int data_d[];}; layout (push_constant) uniform parameter { uint ncols; + uint nrows; uint order; } p; @@ -26,10 +27,9 @@ void swap(uint idx0, uint idx1) { dst_row[idx1] = tmp; } -void argsort(bool needs_bounds_check) { +void argsort(bool needs_bounds_check, const uint row) { // bitonic sort const int col = int(gl_LocalInvocationID.x); - const uint row = gl_WorkGroupID.y; const uint row_offset = row * p.ncols; @@ -72,8 +72,16 @@ void argsort(bool needs_bounds_check) { void main() { if (p.ncols == BLOCK_SIZE) { - argsort(false); + uint row = gl_WorkGroupID.y; + while (row < p.nrows) { + argsort(false, row); + row += gl_WorkGroupSize.y * gl_NumWorkGroups.y; + } } else { - argsort(true); + uint row = gl_WorkGroupID.y; + while (row < p.nrows) { + argsort(true, row); + row += gl_WorkGroupSize.y * gl_NumWorkGroups.y; + } } } diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_funcs.glsl b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_funcs.glsl index 0d98f5a9..09676a62 100644 --- a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_funcs.glsl +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_funcs.glsl @@ -437,7 +437,7 @@ vec4 dequantize4(uint ib, uint iqs, uint a_offset) { #if defined(DATA_A_MXFP4) vec2 dequantize(uint ib, uint iqs, uint a_offset) { const uint vui = uint(data_a[a_offset + ib].qs[iqs]); - return vec2(kvalues_mxfp4[vui & 0xF], kvalues_mxfp4[vui >> 4]); + return vec2(kvalues_mxfp4[vui & 0xF], kvalues_mxfp4[vui >> 4]) * 0.5; } vec4 dequantize4(uint ib, uint iqs, uint a_offset) { vec2 v0 = dequantize(ib, iqs, a_offset); @@ -488,9 +488,9 @@ vec2 dequantize(uint ib, uint iqs, uint a_offset) { const uvec2 qs = uvec2(data_a[a_offset + ib].qs[qsi], data_a[a_offset + ib].qs[qsi + 1]); const uint scales = data_a[a_offset + ib].scales[scalesi]; - const vec2 d = vec2(data_a[a_offset + ib].d); + const vec2 dm = vec2(data_a[a_offset + ib].dm); - return d.x * float(scales & 0xF) * vec2((qs >> qsshift) & 3) - d.y * float(scales >> 4); + return dm.x * float(scales & 0xF) * vec2((qs >> qsshift) & 3) - dm.y * float(scales >> 4); } vec2 get_dm(uint ib, uint a_offset) { return vec2(1, 0); @@ -529,7 +529,7 @@ vec2 dequantize(uint ib, uint iqs, uint a_offset) { const uint is = 2 * n + b; // 0..7 const uint qsi = n * 32 + (iqs % 16) * 2; // 0,2,4..126 - const vec2 loadd = vec2(data_a[a_offset + ib].d); + const vec2 loadd = vec2(data_a[a_offset + ib].dm); const uint scidx0 = (is < 4) ? is : (is + 4); const uint scidx1 = (is < 4) ? is : (is - 4); @@ -567,7 +567,7 @@ vec2 dequantize(uint ib, uint iqs, uint a_offset) { const uint8_t hm = uint8_t(1 << (iqs / 16)); - const vec2 loadd = vec2(data_a[a_offset + ib].d); + const vec2 loadd = vec2(data_a[a_offset + ib].dm); const uint scidx0 = (is < 4) ? is : (is + 4); const uint scidx1 = (is < 4) ? is : (is - 4); diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_funcs_cm2.glsl b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_funcs_cm2.glsl index 67baedf7..8ac6482d 100644 --- a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_funcs_cm2.glsl +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_funcs_cm2.glsl @@ -120,7 +120,7 @@ layout(buffer_reference, std430, buffer_reference_align = 16) buffer decodeBufQ2 float16_t dequantFuncQ2_K(const in decodeBufQ2_K bl, const in uint blockCoords[2], const in uint coordInBlock[2]) { decodeBufQ2_K_packed16 bl16 = decodeBufQ2_K_packed16(bl); - const f16vec2 d = bl.block.d; + const f16vec2 dm = bl.block.dm; const uint idx = coordInBlock[1]; const uint scalesi = (idx & 0xF0) >> 4; // 0..15 @@ -131,7 +131,7 @@ float16_t dequantFuncQ2_K(const in decodeBufQ2_K bl, const in uint blockCoords[2 qs = unpack8(qs)[idx & 1]; const uint scales = bl.block.scales[scalesi]; - float16_t ret = d.x * float16_t(scales & 0xF) * float16_t(qs) - d.y * float16_t(scales >> 4); + float16_t ret = dm.x * float16_t(scales & 0xF) * float16_t(qs) - dm.y * float16_t(scales >> 4); return ret; } @@ -680,7 +680,7 @@ float16_t dequantFuncMXFP4(const in decodeBufMXFP4 bl, const in uint blockCoords uint32_t qs = bl.block.qs[iqs]; qs >>= shift; qs &= 0xF; - float16_t ret = float16_t(kvalues_mxfp4[qs] * d); + float16_t ret = float16_t(kvalues_mxfp4[qs] * d * 0.5); return ret; } #endif diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_mxfp4.comp b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_mxfp4.comp index ffba5a77..3194ba29 100644 --- a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_mxfp4.comp +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_mxfp4.comp @@ -26,7 +26,7 @@ void main() { const float d = e8m0_to_fp32(data_a[ib].e); [[unroll]] for (uint l = 0; l < 8; ++l) { - data_b[b_idx + l + 0] = D_TYPE(d * kvalues_mxfp4[data_a[ib].qs[q_idx + l] & 0xF]); - data_b[b_idx + l + 16] = D_TYPE(d * kvalues_mxfp4[data_a[ib].qs[q_idx + l] >> 4]); + data_b[b_idx + l + 0] = D_TYPE(d * 0.5 * float(kvalues_mxfp4[data_a[ib].qs[q_idx + l] & 0xF])); + data_b[b_idx + l + 16] = D_TYPE(d * 0.5 * float(kvalues_mxfp4[data_a[ib].qs[q_idx + l] >> 4])); } } diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q2_k.comp b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q2_k.comp index 58dc2e5d..dc05a783 100644 --- a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q2_k.comp +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q2_k.comp @@ -24,8 +24,8 @@ void main() { const uint ql_idx = 32 * ip + il; const uint8_t qs = data_a[i].qs[32 * ip + il]; - FLOAT_TYPE dall = FLOAT_TYPE(data_a[i].d.x); - FLOAT_TYPE dmin = FLOAT_TYPE(data_a[i].d.y); + FLOAT_TYPE dall = FLOAT_TYPE(data_a[i].dm.x); + FLOAT_TYPE dmin = FLOAT_TYPE(data_a[i].dm.y); data_b[y_idx + 0] = D_TYPE(dall * FLOAT_TYPE((data_a[i].scales[is+0] & 0xF) * ((qs >> 0) & 3)) - dmin * FLOAT_TYPE(data_a[i].scales[is+0] >> 4)); data_b[y_idx + 32] = D_TYPE(dall * FLOAT_TYPE((data_a[i].scales[is+2] & 0xF) * ((qs >> 2) & 3)) - dmin * FLOAT_TYPE(data_a[i].scales[is+2] >> 4)); data_b[y_idx + 64] = D_TYPE(dall * FLOAT_TYPE((data_a[i].scales[is+4] & 0xF) * ((qs >> 4) & 3)) - dmin * FLOAT_TYPE(data_a[i].scales[is+4] >> 4)); diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q4_k.comp b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q4_k.comp index 8b7be557..0f23dc0a 100644 --- a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q4_k.comp +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q4_k.comp @@ -20,8 +20,8 @@ void main() { const uint is = 2 * il; const uint n = 4; - const FLOAT_TYPE dall = FLOAT_TYPE(data_a[ib].d.x); - const FLOAT_TYPE dmin = FLOAT_TYPE(data_a[ib].d.y); + const FLOAT_TYPE dall = FLOAT_TYPE(data_a[ib].dm.x); + const FLOAT_TYPE dmin = FLOAT_TYPE(data_a[ib].dm.y); const uint y_idx = ib * QUANT_K + 64 * il + n * ir; const uint qs_idx = 32*il + n * ir; diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q5_k.comp b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q5_k.comp index 6bc04670..970469a6 100644 --- a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q5_k.comp +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/dequant_q5_k.comp @@ -19,8 +19,8 @@ void main() { const uint ir = tid % 16; const uint is = 2 * il; - const FLOAT_TYPE dall = FLOAT_TYPE(data_a[ib].d.x); - const FLOAT_TYPE dmin = FLOAT_TYPE(data_a[ib].d.y); + const FLOAT_TYPE dall = FLOAT_TYPE(data_a[ib].dm.x); + const FLOAT_TYPE dmin = FLOAT_TYPE(data_a[ib].dm.y); const uint y_idx = ib * QUANT_K + 64 * il + 2 * ir; const uint qs_idx = 32*il + 2 * ir; diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q2_k.comp b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q2_k.comp index 03ed25d3..14093c0d 100644 --- a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q2_k.comp +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q2_k.comp @@ -41,9 +41,7 @@ void calc_superblock(const uint a_offset, const uint b_offset, const uint itid, const vec4 qs_u32_4 = vec4(unpack8((qs_u32 >> 4) & 0x03030303)); const vec4 qs_u32_6 = vec4(unpack8((qs_u32 >> 6) & 0x03030303)); - vec2 d = vec2(data_a[ib0 + i].d); - const FLOAT_TYPE dall = FLOAT_TYPE(d.x); - const FLOAT_TYPE dmin = FLOAT_TYPE(d.y); + const FLOAT_TYPE_VEC2 dm = vec2(data_a[ib0 + i].dm); [[unroll]] for (uint j = 0; j < NUM_COLS; ++j) { vec2 b0 = vec2(data_b_v2[(j*p.batch_stride_b + b_offset + y_idx) / 2 + 0]); @@ -75,7 +73,7 @@ void calc_superblock(const uint a_offset, const uint b_offset, const uint itid, fma(FLOAT_TYPE(b96[l]), sccache2[csel][ix][6 + 8*v_im], fma(FLOAT_TYPE(b112[l]), sccache2[csel][ix][7 + 8*v_im], sum2)))))))); } - temp[j][n] = fma(dall, sum1, fma(-dmin, sum2, temp[j][n])); + temp[j][n] = fma(dm.x, sum1, fma(-dm.y, sum2, temp[j][n])); } } } diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q4_k.comp b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q4_k.comp index 21d07d2e..49d91ad5 100644 --- a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q4_k.comp +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q4_k.comp @@ -14,9 +14,7 @@ void calc_superblock(const uint a_offset, const uint b_offset, const uint v_im, [[unroll]] for (uint n = 0; n < num_rows; ++n) { const uint ib0 = a_offset / QUANT_K + (first_row+n)*num_blocks_per_row; - vec2 d = vec2(data_a[ib0 + i].d); - const FLOAT_TYPE dall = FLOAT_TYPE(d.x); - const FLOAT_TYPE dmin = FLOAT_TYPE(d.y); + const FLOAT_TYPE_VEC2 dm = FLOAT_TYPE_VEC2(data_a[ib0 + i].dm); const uint32_t scale0_u32 = data_a_packed16[ib0 + i].scales[v_im ]; const uint32_t scale4_u32 = data_a_packed16[ib0 + i].scales[v_im + 2]; @@ -81,7 +79,7 @@ void calc_superblock(const uint a_offset, const uint b_offset, const uint v_im, fma(FLOAT_TYPE(by10.y), sc2, fma(FLOAT_TYPE(by132.y), sc3, fma(FLOAT_TYPE(by20.y), sc6, fma(FLOAT_TYPE(by232.y), sc7, fma(FLOAT_TYPE(by10.z), sc2, fma(FLOAT_TYPE(by132.z), sc3, fma(FLOAT_TYPE(by20.z), sc6, fma(FLOAT_TYPE(by232.z), sc7, fma(FLOAT_TYPE(by10.w), sc2, fma(FLOAT_TYPE(by132.w), sc3, fma(FLOAT_TYPE(by20.w), sc6, FLOAT_TYPE(by232.w) * sc7))))))))))))))); - temp[j][n] = fma(dall, fma(sx, sc0, fma(sy, sc1, fma(sz, sc4, sw * sc5))), fma(-dmin, smin, temp[j][n])); + temp[j][n] = fma(dm.x, fma(sx, sc0, fma(sy, sc1, fma(sz, sc4, sw * sc5))), fma(-dm.y, smin, temp[j][n])); } } } diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q5_k.comp b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q5_k.comp index 9e46c89a..0d61b496 100644 --- a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q5_k.comp +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mat_vec_q5_k.comp @@ -14,9 +14,7 @@ void calc_superblock(const uint a_offset, const uint b_offset, const uint v_im, [[unroll]] for (uint n = 0; n < num_rows; ++n) { const uint ib0 = a_offset / QUANT_K + (first_row+n)*num_blocks_per_row; - vec2 d = vec2(data_a[ib0 + i].d); - const FLOAT_TYPE dall = FLOAT_TYPE(d.x); - const FLOAT_TYPE dmin = FLOAT_TYPE(d.y); + const FLOAT_TYPE_VEC2 dm = FLOAT_TYPE_VEC2(data_a[ib0 + i].dm); const uint32_t scale0_u32 = data_a_packed16[ib0 + i].scales[v_im ]; const uint32_t scale4_u32 = data_a_packed16[ib0 + i].scales[v_im + 2]; @@ -113,7 +111,7 @@ void calc_superblock(const uint a_offset, const uint b_offset, const uint v_im, fma(FLOAT_TYPE(by132.x) + FLOAT_TYPE(by132.y) + FLOAT_TYPE(by148.x) + FLOAT_TYPE(by148.y), sc3, fma(FLOAT_TYPE(by20.x) + FLOAT_TYPE(by20.y) + FLOAT_TYPE(by216.x) + FLOAT_TYPE(by216.y), sc6, (FLOAT_TYPE(by232.x) + FLOAT_TYPE(by232.y) + FLOAT_TYPE(by248.x) + FLOAT_TYPE(by248.y)) * sc7))); - temp[j][n] = fma(dall, fma(sx, sc0, fma(sy, sc1, fma(sz, sc4, sw * sc5))), fma(-dmin, smin, temp[j][n])); + temp[j][n] = fma(dm.x, fma(sx, sc0, fma(sy, sc1, fma(sz, sc4, sw * sc5))), fma(-dm.y, smin, temp[j][n])); } } } diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm.comp b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm.comp index a20788c4..d260969f 100644 --- a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm.comp +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm.comp @@ -120,81 +120,11 @@ shared FLOAT_TYPE_VEC2 buf_b[BN * SHMEM_STRIDE]; #define NUM_WARPS (BLOCK_SIZE / WARP) -#ifdef MUL_MAT_ID -shared u16vec2 row_ids[BN]; -uint _ne1; - -#ifdef MUL_MAT_ID_USE_SUBGROUPS -shared uvec4 ballots_sh[NUM_WARPS]; - -void load_row_ids(uint expert_idx, bool nei0_is_pow2, uint ic) { - _ne1 = 0; - uint num_elements = p.nei1 * p.nei0; - uint nei0shift = findLSB(p.nei0); - - uint ids[16]; - uint iter = 0; - - for (uint j = 0; j < num_elements; j += BLOCK_SIZE) { - // prefetch up to 16 elements - if (iter == 0) { - [[unroll]] for (uint k = 0; k < 16; ++k) { - uint i = j + gl_LocalInvocationIndex + k*BLOCK_SIZE; - bool in_range = i < num_elements; - uint ii1; - if (nei0_is_pow2) { - ii1 = i >> nei0shift; - } else { - ii1 = i / p.nei0; - } - uint ii0 = i - ii1 * p.nei0; - ids[k] = in_range ? data_ids[ii1*p.nbi1 + ii0] : 0; - } - } - uint i = j + gl_LocalInvocationIndex; - bool in_range = i < num_elements; - uint ii1; - if (nei0_is_pow2) { - ii1 = i >> nei0shift; - } else { - ii1 = i / p.nei0; - } - uint ii0 = i - ii1 * p.nei0; - uint id = ids[iter++]; - uvec4 ballot = subgroupBallot(in_range && id == expert_idx); - - ballots_sh[gl_SubgroupID] = ballot; - barrier(); - - uint subgroup_base = 0; - uint total = 0; - for (uint k = 0; k < gl_NumSubgroups; ++k) { - if (k == gl_SubgroupID) { - subgroup_base = total; - } - total += subgroupBallotBitCount(ballots_sh[k]); - } - barrier(); - - uint idx = subgroup_base + subgroupBallotExclusiveBitCount(ballot); - if (in_range && id == expert_idx && _ne1 + idx >= ic * BN && _ne1 + idx < (ic + 1) * BN) { - row_ids[_ne1 + idx - ic * BN] = u16vec2(ii0, ii1); - } - _ne1 += total; - iter &= 15; - if (_ne1 >= (ic + 1) * BN) { - break; - } - } - barrier(); -} -#endif // MUL_MAT_ID_USE_SUBGROUPS -#endif // MUL_MAT_ID - #ifdef COOPMAT shared ACC_TYPE coopmat_stage[TM * TN * NUM_WARPS]; #endif +#include "mul_mm_id_funcs.glsl" #include "mul_mm_funcs.glsl" void main() { diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm_funcs.glsl b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm_funcs.glsl index 0ebfbd64..ee5ded2e 100644 --- a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm_funcs.glsl +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm_funcs.glsl @@ -134,15 +134,15 @@ void load_a_to_shmem(const uint pos_a, const uint row, const uint col, const uin const uint ib = idx / 128; // 2 values per idx const uint iqs = idx % 128; // 0..127 - const uint qsi = (iqs / 64) * 32 + (iqs % 16) * 2; // 0,2,4..30 + const uint qsi = (iqs / 64) * 16 + (iqs % 16); // 0..15 const uint scalesi = iqs / 8; // 0..15 const uint qsshift = ((iqs % 64) / 16) * 2; // 0,2,4,6 - const uvec2 qs = uvec2(data_a[ib].qs[qsi], data_a[ib].qs[qsi + 1]); + const uvec2 qs = uvec2(unpack8(data_a_packed16[ib].qs[qsi])); const uint scales = data_a[ib].scales[scalesi]; - const vec2 d = vec2(data_a[ib].d); + const vec2 dm = vec2(data_a[ib].dm); - const vec2 v = d.x * float(scales & 0xF) * vec2((qs >> qsshift) & 3) - d.y * float(scales >> 4); + const vec2 v = dm.x * float(scales & 0xF) * vec2((qs >> qsshift) & 3) - dm.y * float(scales >> 4); buf_a[buf_idx] = FLOAT_TYPE_VEC2(v.xy); #elif defined(DATA_A_Q3_K) @@ -179,7 +179,7 @@ void load_a_to_shmem(const uint pos_a, const uint row, const uint col, const uin const uint is = 2 * n + b; // 0..7 const uint qsi = n * 32 + (iqs % 16) * 2; // 0,2,4..126 - const vec2 loadd = vec2(data_a[ib].d); + const vec2 loadd = vec2(data_a[ib].dm); const uint scidx0 = (is < 4) ? is : (is + 4); const uint scidx1 = (is < 4) ? is : (is - 4); @@ -215,7 +215,7 @@ void load_a_to_shmem(const uint pos_a, const uint row, const uint col, const uin const uint8_t hm = uint8_t(1 << (iqs / 16)); - const vec2 loadd = vec2(data_a[ib].d); + const vec2 loadd = vec2(data_a[ib].dm); const uint scidx0 = (is < 4) ? is : (is + 4); const uint scidx1 = (is < 4) ? is : (is - 4); @@ -468,7 +468,7 @@ void load_a_to_shmem(const uint pos_a, const uint row, const uint col, const uin const uint ib = idx / 8; const uint iqs = (idx & 0x07) * 2; - const float d = e8m0_to_fp32(data_a[ib].e); + const float d = e8m0_to_fp32(data_a[ib].e) * 0.5; const uint vui = uint(data_a[ib].qs[iqs]); const uint vui2 = uint(data_a[ib].qs[iqs+1]); diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm_id_funcs.glsl b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm_id_funcs.glsl new file mode 100644 index 00000000..1d0e84ac --- /dev/null +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mm_id_funcs.glsl @@ -0,0 +1,70 @@ +#ifdef MUL_MAT_ID +shared u16vec2 row_ids[BN]; +uint _ne1; + +#ifdef MUL_MAT_ID_USE_SUBGROUPS +shared uvec4 ballots_sh[NUM_WARPS]; + +void load_row_ids(uint expert_idx, bool nei0_is_pow2, uint ic) { + _ne1 = 0; + uint num_elements = p.nei1 * p.nei0; + uint nei0shift = findLSB(p.nei0); + + uint ids[16]; + uint iter = 0; + + for (uint j = 0; j < num_elements; j += BLOCK_SIZE) { + // prefetch up to 16 elements + if (iter == 0) { + [[unroll]] for (uint k = 0; k < 16; ++k) { + uint i = j + gl_LocalInvocationIndex + k*BLOCK_SIZE; + bool in_range = i < num_elements; + uint ii1; + if (nei0_is_pow2) { + ii1 = i >> nei0shift; + } else { + ii1 = i / p.nei0; + } + uint ii0 = i - ii1 * p.nei0; + ids[k] = in_range ? data_ids[ii1*p.nbi1 + ii0] : 0; + } + } + uint i = j + gl_LocalInvocationIndex; + bool in_range = i < num_elements; + uint ii1; + if (nei0_is_pow2) { + ii1 = i >> nei0shift; + } else { + ii1 = i / p.nei0; + } + uint ii0 = i - ii1 * p.nei0; + uint id = ids[iter++]; + uvec4 ballot = subgroupBallot(in_range && id == expert_idx); + + ballots_sh[gl_SubgroupID] = ballot; + barrier(); + + uint subgroup_base = 0; + uint total = 0; + for (uint k = 0; k < gl_NumSubgroups; ++k) { + if (k == gl_SubgroupID) { + subgroup_base = total; + } + total += subgroupBallotBitCount(ballots_sh[k]); + } + barrier(); + + uint idx = subgroup_base + subgroupBallotExclusiveBitCount(ballot); + if (in_range && id == expert_idx && _ne1 + idx >= ic * BN && _ne1 + idx < (ic + 1) * BN) { + row_ids[_ne1 + idx - ic * BN] = u16vec2(ii0, ii1); + } + _ne1 += total; + iter &= 15; + if (_ne1 >= (ic + 1) * BN) { + break; + } + } + barrier(); +} +#endif // MUL_MAT_ID_USE_SUBGROUPS +#endif // MUL_MAT_ID diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq.comp b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq.comp index b5d761c0..d955b4fc 100644 --- a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq.comp +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq.comp @@ -10,10 +10,9 @@ #extension GL_EXT_shader_explicit_arithmetic_types_float16 : require #endif -#ifdef COOPMAT -#extension GL_KHR_cooperative_matrix : enable -#extension GL_KHR_memory_scope_semantics : enable +#if defined(MUL_MAT_ID_USE_SUBGROUPS) #extension GL_KHR_shader_subgroup_basic : enable +#extension GL_KHR_shader_subgroup_ballot : enable #endif #ifdef MUL_MAT_ID @@ -24,7 +23,10 @@ layout(local_size_x_id = 0, local_size_y = 1, local_size_z = 1) in; -layout (binding = 0) readonly buffer A {A_TYPE_PACKED16 data_a[];}; +layout (binding = 0) readonly buffer A {A_TYPE data_a[];}; +#if defined(A_TYPE_PACKED16) +layout (binding = 0) readonly buffer A_PACKED16 {A_TYPE_PACKED16 data_a_packed16[];}; +#endif #if defined(A_TYPE_PACKED32) layout (binding = 0) readonly buffer A_PACKED32 {A_TYPE_PACKED32 data_a_packed32[];}; #endif @@ -76,40 +78,31 @@ layout (constant_id = 10) const uint WARP = 32; #define BK 32 -#ifdef COOPMAT -#define SHMEM_STRIDE (BK / 4 + 4) -#else -#define SHMEM_STRIDE (BK / 4 + 1) -#endif +#define MMQ_SHMEM -shared int32_t buf_a_qs[BM * SHMEM_STRIDE]; - -#ifndef COOPMAT -#if QUANT_AUXF == 1 -shared FLOAT_TYPE buf_a_dm[BM]; -#else -shared FLOAT_TYPE_VEC2 buf_a_dm[BM]; -#endif -#endif - -shared int32_t buf_b_qs[BN * SHMEM_STRIDE]; -#ifndef COOPMAT -shared FLOAT_TYPE_VEC2 buf_b_ds[BN]; -#endif - -#define LOAD_VEC_A (4 * QUANT_R) -#define LOAD_VEC_B 16 +#include "mul_mmq_shmem_types.glsl" #ifdef MUL_MAT_ID -shared u16vec2 row_ids[4096]; -#endif // MUL_MAT_ID +#define BK_STEP 1 +#else +#ifndef BK_STEP +#define BK_STEP 4 +#endif +#endif + +// Shared memory cache +shared block_a_cache buf_a[BM * BK_STEP]; +shared block_b_cache buf_b[BN * BK_STEP]; +// Register cache +block_a_cache cache_a[WMITER * TM]; +block_b_cache cache_b; + +#define LOAD_VEC_A (4 * QUANT_R_MMQ) +#define LOAD_VEC_B 16 #define NUM_WARPS (BLOCK_SIZE / WARP) -#ifdef COOPMAT -shared ACC_TYPE coopmat_stage[TM * TN * NUM_WARPS]; -#endif - +#include "mul_mm_id_funcs.glsl" #include "mul_mmq_funcs.glsl" void main() { @@ -139,26 +132,12 @@ void main() { const uint WNITER = (WM * WN) / (WARP * TM * TN * WMITER); const uint WSUBM = WM / WMITER; const uint WSUBN = WN / WNITER; - -#ifdef COOPMAT - const uint warp_i = gl_SubgroupID; - - const uint tiw = gl_SubgroupInvocationID; - - const uint cms_per_row = WM / TM; - const uint cms_per_col = WN / TN; - - const uint storestride = WARP / TM; - const uint store_r = tiw % TM; - const uint store_c = tiw / TM; -#else const uint warp_i = gl_LocalInvocationID.x / WARP; const uint tiw = gl_LocalInvocationID.x % WARP; const uint tiwr = tiw % (WSUBM / TM); const uint tiwc = tiw / (WSUBM / TM); -#endif const uint warp_r = warp_i % (BM / WM); const uint warp_c = warp_i / (BM / WM); @@ -172,17 +151,27 @@ void main() { const uint loadstride_b = BLOCK_SIZE * LOAD_VEC_B / BK; #ifdef MUL_MAT_ID - uint _ne1 = 0; - for (uint ii1 = 0; ii1 < p.nei1; ii1++) { - for (uint ii0 = 0; ii0 < p.nei0; ii0++) { +#ifdef MUL_MAT_ID_USE_SUBGROUPS + if (bitCount(p.nei0) == 1) { + load_row_ids(expert_idx, true, ic); + } else { + load_row_ids(expert_idx, false, ic); + } +#else + _ne1 = 0; + for (uint ii1 = 0; ii1 < p.nei1 && _ne1 < (ic + 1) * BN; ii1++) { + for (uint ii0 = 0; ii0 < p.nei0 && _ne1 < (ic + 1) * BN; ii0++) { if (data_ids[ii1*p.nbi1 + ii0] == expert_idx) { - row_ids[_ne1] = u16vec2(ii0, ii1); + if (_ne1 >= ic * BN) { + row_ids[_ne1 - ic * BN] = u16vec2(ii0, ii1); + } _ne1++; } } } barrier(); +#endif // Workgroup has no work if (ic * BN >= _ne1) return; @@ -209,159 +198,70 @@ void main() { uint pos_b_ib = (batch_idx * p.batch_stride_b + ic * BN * p.stride_b + start_k) / BK; #endif -#ifdef COOPMAT - coopmat cache_a; - coopmat cache_b; - coopmat cm_result; - - coopmat factors[cms_per_row * cms_per_col]; - - coopmat sums[cms_per_row * cms_per_col]; - - [[unroll]] for (uint i = 0; i < cms_per_row * cms_per_col; i++) { - sums[i] = coopmat(0.0f); - } -#else - int32_t cache_a_qs[WMITER * TM * BK / 4]; - - int32_t cache_b_qs[TN * BK / 4]; - ACC_TYPE sums[WMITER * TM * WNITER * TN]; [[unroll]] for (uint i = 0; i < WMITER*TM*WNITER*TN; i++) { sums[i] = ACC_TYPE(0.0f); } -#endif -#if QUANT_AUXF == 1 - FLOAT_TYPE cache_a_dm[WMITER * TM]; -#else - FLOAT_TYPE_VEC2 cache_a_dm[WMITER * TM]; -#endif - - FLOAT_TYPE_VEC2 cache_b_ds[TN]; - - for (uint block = start_k; block < end_k; block += BK) { + for (uint block = start_k; block < end_k; block += BK * BK_STEP) { [[unroll]] for (uint l = 0; loadc_a + l < BM; l += loadstride_a) { - const uint ib = pos_a_ib + (loadc_a + l) * p.stride_a / BK; - const uint iqs = loadr_a; const uint buf_ib = loadc_a + l; + const uint ib = pos_a_ib + buf_ib * p.stride_a / BK; + const uint iqs = loadr_a; - if (iqs == 0) { -#if QUANT_AUXF == 1 - buf_a_dm[buf_ib] = get_d(ib); -#else - buf_a_dm[buf_ib] = get_dm(ib); -#endif + [[unroll]] for (uint k_step = 0; k_step < BK_STEP; k_step++) { + block_a_to_shmem(k_step * BM + buf_ib, ib + k_step, iqs); } -#if QUANT_R == 1 - buf_a_qs[buf_ib * SHMEM_STRIDE + iqs] = repack(ib, iqs); -#else - const i32vec2 vals = repack(ib, iqs); - buf_a_qs[buf_ib * SHMEM_STRIDE + iqs ] = vals.x; - buf_a_qs[buf_ib * SHMEM_STRIDE + iqs + 4] = vals.y; -#endif } [[unroll]] for (uint l = 0; loadc_b + l < BN; l += loadstride_b) { -#ifdef MUL_MAT_ID - const u16vec2 row_idx = row_ids[ic * BN + loadc_b + l]; - const uint idx = pos_b_ib + row_idx.y * p.batch_stride_b / LOAD_VEC_B + (row_idx.x % p.ne11) * p.stride_b / LOAD_VEC_B + loadr_b; - const uint ib = idx / 8; - const uint iqs = idx & 0x7; -#else - const uint ib = pos_b_ib + (loadc_b + l) * p.stride_b / BK; - const uint ib_outer = ib / 4; - const uint ib_inner = ib % 4; - - const uint iqs = loadr_b; -#endif - const uint buf_ib = loadc_b + l; - if (iqs == 0) { - buf_b_ds[buf_ib] = FLOAT_TYPE_VEC2(data_b[ib_outer].ds[ib_inner]); +#ifdef MUL_MAT_ID + const u16vec2 row_idx = row_ids[buf_ib]; + const uint ib = pos_b_ib + row_idx.y * p.batch_stride_b / BK + (row_idx.x % p.ne11) * p.stride_b / BK; +#else + const uint ib = pos_b_ib + buf_ib * p.stride_b / BK; +#endif + const uint iqs = loadr_b; + + [[unroll]] for (uint k_step = 0; k_step < BK_STEP; k_step++) { + block_b_to_shmem(k_step * BN + buf_ib, ib + k_step, iqs); } - const ivec4 values = data_b[ib_outer].qs[ib_inner * 2 + iqs]; - buf_b_qs[buf_ib * SHMEM_STRIDE + iqs * 4 ] = values.x; - buf_b_qs[buf_ib * SHMEM_STRIDE + iqs * 4 + 1] = values.y; - buf_b_qs[buf_ib * SHMEM_STRIDE + iqs * 4 + 2] = values.z; - buf_b_qs[buf_ib * SHMEM_STRIDE + iqs * 4 + 3] = values.w; } barrier(); - pos_a_ib += 1; - pos_b_ib += 1; + pos_a_ib += BK_STEP; + pos_b_ib += BK_STEP; -#ifdef COOPMAT - [[unroll]] for (uint cm_row = 0; cm_row < cms_per_row; cm_row++) { - const uint ib_a = warp_r * WM + cm_row * TM; + for (uint k_step = 0; k_step < BK_STEP; k_step++) { // Load from shared into cache - coopMatLoad(cache_a, buf_a_qs, ib_a * SHMEM_STRIDE, SHMEM_STRIDE, gl_CooperativeMatrixLayoutRowMajor); - - // TODO: only cache values that are actually needed - [[unroll]] for (uint t_idx = 0; t_idx < TM; t_idx++) { - cache_a_dm[t_idx] = buf_a_dm[ib_a + t_idx]; - } - - [[unroll]] for (uint cm_col = 0; cm_col < cms_per_col; cm_col++) { - const uint ib_b = warp_c * WN + cm_col * TN; - coopMatLoad(cache_b, buf_b_qs, ib_b * SHMEM_STRIDE, SHMEM_STRIDE, gl_CooperativeMatrixLayoutColumnMajor); - - // TODO: only cache values that are actually needed - [[unroll]] for (uint t_idx = 0; t_idx < TN; t_idx++) { - cache_b_dm[t_idx] = buf_b_d[ib_b + t_idx]; - } - - cm_result = coopmat(0); - cm_result = coopMatMulAdd(cache_a, cache_b, cm_result); - - [[unroll]] for (uint col = 0; col < TN; col += storestride) { - coopmat_stage[warp_i * TM * TN + (store_c + col) * TM + store_r] = ACC_TYPE(float(cache_a_d[store_r]) * float(cache_b_d[store_c + col])); - } - - coopMatLoad(factors, coopmat_stage, warp_i * TM * TN, TM, gl_CooperativeMatrixLayoutColumnMajor); - sums[cm_col * cms_per_row + cm_row] += factors * coopmat(cm_result); - } - } -#else - // Load from shared into cache - [[unroll]] for (uint wsir = 0; wsir < WMITER; wsir++) { - [[unroll]] for (uint cr = 0; cr < TM; cr++) { - const uint ib = warp_r * WM + wsir * WSUBM + tiwr * TM + cr; - cache_a_dm[wsir * TM + cr] = buf_a_dm[ib]; - [[unroll]] for (uint idx_k = 0; idx_k < BK / 4; idx_k++) { - cache_a_qs[(wsir * TM + cr) * (BK / 4) + idx_k] = buf_a_qs[ib * SHMEM_STRIDE + idx_k]; - } - } - } - - [[unroll]] for (uint wsic = 0; wsic < WNITER; wsic++) { - [[unroll]] for (uint cc = 0; cc < TN; cc++) { - const uint ib = warp_c * WN + wsic * WSUBN + tiwc * TN + cc; - cache_b_ds[cc] = buf_b_ds[ib]; - [[unroll]] for (uint idx_k = 0; idx_k < BK / 4; idx_k++) { - cache_b_qs[cc * (BK / 4) + idx_k] = buf_b_qs[ib * SHMEM_STRIDE + idx_k]; - } - } - [[unroll]] for (uint wsir = 0; wsir < WMITER; wsir++) { - [[unroll]] for (uint cc = 0; cc < TN; cc++) { - [[unroll]] for (uint cr = 0; cr < TM; cr++) { - const uint cache_a_idx = wsir * TM + cr; - const uint sums_idx = (wsic * TN + cc) * (WMITER * TM) + wsir * TM + cr; - int32_t q_sum = 0; - [[unroll]] for (uint idx_k = 0; idx_k < BK / 4; idx_k++) { - q_sum += dotPacked4x8EXT(cache_a_qs[cache_a_idx * (BK / 4) + idx_k], - cache_b_qs[cc * (BK / 4) + idx_k]); - } + [[unroll]] for (uint cr = 0; cr < TM; cr++) { + const uint reg_ib = wsir * TM + cr; + const uint buf_ib = warp_r * WM + wsir * WSUBM + tiwr * TM + cr; - sums[sums_idx] += mul_q8_1(q_sum, cache_a_dm[cache_a_idx], cache_b_ds[cc], 1); + block_a_to_registers(reg_ib, k_step * BM + buf_ib); + } + } + + [[unroll]] for (uint wsic = 0; wsic < WNITER; wsic++) { + [[unroll]] for (uint cc = 0; cc < TN; cc++) { + const uint ib = k_step * BN + warp_c * WN + wsic * WSUBN + tiwc * TN + cc; + block_b_to_registers(ib); + + [[unroll]] for (uint wsir = 0; wsir < WMITER; wsir++) { + [[unroll]] for (uint cr = 0; cr < TM; cr++) { + const uint cache_a_idx = wsir * TM + cr; + const uint sums_idx = (wsic * TN + cc) * (WMITER * TM) + wsir * TM + cr; + + sums[sums_idx] += mmq_dot_product(cache_a_idx); + } } } } } -#endif barrier(); } @@ -373,54 +273,6 @@ void main() { const uint offsets = batch_idx * p.batch_stride_d + ik * p.batch_stride_d * gl_NumWorkGroups.z; #endif -#ifdef COOPMAT -#ifdef MUL_MAT_ID - [[unroll]] for (uint cm_row = 0; cm_row < cms_per_row; cm_row++) { - [[unroll]] for (uint cm_col = 0; cm_col < cms_per_col; cm_col++) { - coopMatStore(sums[cm_col * cms_per_row + cm_row], coopmat_stage, warp_i * TM * TN, TM, gl_CooperativeMatrixLayoutColumnMajor); - - [[unroll]] for (uint col = 0; col < BN; col += storestride) { - const uint row_i = dc + cm_col * TN + col + store_c; - if (row_i >= _ne1) break; - - const u16vec2 row_idx = row_ids[row_i]; - - data_d[row_idx.y * p.batch_stride_d + row_idx.x * p.stride_d + dr + cm_row * TM + store_r] = D_TYPE(coopmat_stage[warp_i * TM * TN + (col + store_c) * TM + store_r]); - } - } - } -#else - const bool is_aligned = p.stride_d % 4 == 0; // Assumption: D_TYPE == float - - [[unroll]] for (uint cm_row = 0; cm_row < cms_per_row; cm_row++) { - [[unroll]] for (uint cm_col = 0; cm_col < cms_per_col; cm_col++) { - const bool is_in_bounds = dr + (cm_row + 1) * TM <= p.M && dc + (cm_col + 1) * TN <= p.N; - - if (is_aligned && is_in_bounds) { - // Full coopMat is within bounds and stride_d is aligned with 16B - coopmat cm_dtype = coopmat(sums[cm_col * cms_per_row + cm_row]); - coopMatStore(cm_dtype, data_d, offsets + (dc + cm_col * TN) * p.stride_d + dr + cm_row * TM, p.stride_d, gl_CooperativeMatrixLayoutColumnMajor); - } else if (is_in_bounds) { - // Full coopMat is within bounds, but stride_d is not aligned - coopMatStore(sums[cm_col * cms_per_row + cm_row], coopmat_stage, warp_i * TM * TN, TM, gl_CooperativeMatrixLayoutColumnMajor); - - [[unroll]] for (uint col = 0; col < TN; col += storestride) { - data_d[offsets + (dc + cm_col * TN + col + store_c) * p.stride_d + dr + cm_row * TM + store_r] = D_TYPE(coopmat_stage[warp_i * TM * TN + (col + store_c) * TM + store_r]); - } - } else if (dr + cm_row * TM < p.M && dc + cm_col * TN < p.N) { - // Partial coopMat is within bounds - coopMatStore(sums[cm_col * cms_per_row + cm_row], coopmat_stage, warp_i * TM * TN, TM, gl_CooperativeMatrixLayoutColumnMajor); - - [[unroll]] for (uint col = 0; col < TN; col += storestride) { - if (dr + cm_row * TM + store_r < p.M && dc + cm_col * TN + col + store_c < p.N) { - data_d[offsets + (dc + cm_col * TN + col + store_c) * p.stride_d + dr + cm_row * TM + store_r] = D_TYPE(coopmat_stage[warp_i * TM * TN + (col + store_c) * TM + store_r]); - } - } - } - } - } -#endif // MUL_MAT_ID -#else [[unroll]] for (uint wsic = 0; wsic < WNITER; wsic++) { [[unroll]] for (uint wsir = 0; wsir < WMITER; wsir++) { @@ -431,19 +283,21 @@ void main() { const uint row_i = dc_warp + cc; if (row_i >= _ne1) break; - const u16vec2 row_idx = row_ids[row_i]; + const u16vec2 row_idx = row_ids[row_i - ic * BN]; #endif // MUL_MAT_ID [[unroll]] for (uint cr = 0; cr < TM; cr++) { + const uint sums_idx = (wsic * TN + cc) * WMITER * TM + wsir * TM + cr; #ifdef MUL_MAT_ID - data_d[row_idx.y * p.batch_stride_d + row_idx.x * p.stride_d + dr_warp + cr] = D_TYPE(sums[(wsic * TN + cc) * (WMITER * TM) + wsir * TM + cr]); + if (dr_warp + cr < p.M) { + data_d[row_idx.y * p.batch_stride_d + row_idx.x * p.stride_d + dr_warp + cr] = D_TYPE(sums[sums_idx].x); + } #else if (dr_warp + cr < p.M && dc_warp + cc < p.N) { - data_d[offsets + (dc_warp + cc) * p.stride_d + dr_warp + cr] = D_TYPE(sums[(wsic * TN + cc) * (WMITER * TM) + wsir * TM + cr]); + data_d[offsets + (dc_warp + cc) * p.stride_d + dr_warp + cr] = D_TYPE(sums[sums_idx].x); } #endif // MUL_MAT_ID } } } } -#endif // COOPMAT } diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq_funcs.glsl b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq_funcs.glsl index fe71eb13..c0c03fed 100644 --- a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq_funcs.glsl +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq_funcs.glsl @@ -6,41 +6,89 @@ // Each iqs value maps to a 32-bit integer -#if defined(DATA_A_Q4_0) +#if defined(DATA_A_Q4_0) || defined(DATA_A_Q4_1) +// 2-byte loads for Q4_0 blocks (18 bytes) +// 4-byte loads for Q4_1 blocks (20 bytes) i32vec2 repack(uint ib, uint iqs) { - // Use 2-byte loads since a q4_0 block (18 bytes) is not divisible by 4 - const u16vec2 quants = u16vec2(data_a[ib].qs[iqs * 2 ], - data_a[ib].qs[iqs * 2 + 1]); +#ifdef DATA_A_Q4_0 + const u16vec2 quants = u16vec2(data_a_packed16[ib].qs[iqs * 2 ], + data_a_packed16[ib].qs[iqs * 2 + 1]); const uint32_t vui = pack32(quants); return i32vec2( vui & 0x0F0F0F0F, (vui >> 4) & 0x0F0F0F0F); +#else // DATA_A_Q4_1 + const uint32_t vui = data_a_packed32[ib].qs[iqs]; + return i32vec2( vui & 0x0F0F0F0F, + (vui >> 4) & 0x0F0F0F0F); +#endif } +#ifdef DATA_A_Q4_0 ACC_TYPE mul_q8_1(const int32_t q_sum, const float da, const vec2 dsb, const int32_t sum_divisor) { return ACC_TYPE(da * (float(q_sum) * dsb.x - (8 / sum_divisor) * dsb.y)); } -#endif - -#if defined(DATA_A_Q4_1) -i32vec2 repack(uint ib, uint iqs) { - // Use 4-byte loads since a q4_1 block (20 bytes) is divisible by 4 - const uint32_t vui = data_a_packed32[ib].qs[iqs]; - return i32vec2( vui & 0x0F0F0F0F, - (vui >> 4) & 0x0F0F0F0F); -} - +#else // DATA_A_Q4_1 ACC_TYPE mul_q8_1(const int32_t q_sum, const vec2 dma, const vec2 dsb, const int32_t sum_divisor) { return ACC_TYPE(float(q_sum) * dma.x * dsb.x + dma.y * dsb.y / sum_divisor); } #endif -#if defined(DATA_A_Q5_0) +#ifdef MMQ_SHMEM +void block_a_to_shmem(const uint buf_ib, const uint ib, const uint iqs) { +#ifdef DATA_A_Q4_0 + buf_a[buf_ib].qs[iqs] = pack32(u16vec2(data_a_packed16[ib].qs[iqs * 2], + data_a_packed16[ib].qs[iqs * 2 + 1])); + + if (iqs == 0) { + buf_a[buf_ib].dm = FLOAT_TYPE(data_a_packed16[ib].d); + } +#else // DATA_A_Q4_1 + buf_a[buf_ib].qs[iqs] = data_a_packed32[ib].qs[iqs]; + + if (iqs == 0) { + buf_a[buf_ib].dm = FLOAT_TYPE_VEC2(data_a_packed32[ib].dm); + } +#endif +} + +void block_a_to_registers(const uint reg_ib, const uint buf_ib) { + cache_a[reg_ib].dm = buf_a[buf_ib].dm; + + [[unroll]] for (uint iqs = 0; iqs < 4; iqs++) { + cache_a[reg_ib].qs[iqs] = buf_a[buf_ib].qs[iqs]; + } +} + +ACC_TYPE mmq_dot_product(const uint ib_a) { + int32_t q_sum = 0; + [[unroll]] for (uint iqs = 0; iqs < 4; iqs++) { + const uint32_t vui = cache_a[ib_a].qs[iqs]; + const i32vec2 qs_a = i32vec2( vui & 0x0F0F0F0F, + (vui >> 4) & 0x0F0F0F0F); + + const int32_t qs_b0 = cache_b.qs[iqs]; + const int32_t qs_b1 = cache_b.qs[iqs + 4]; + + q_sum += dotPacked4x8EXT(qs_a.x, qs_b0); + q_sum += dotPacked4x8EXT(qs_a.y, qs_b1); + } + + return mul_q8_1(q_sum, cache_a[ib_a].dm, cache_b.ds, 1); +} +#endif // MMQ_SHMEM + +#elif defined(DATA_A_Q5_0) || defined(DATA_A_Q5_1) +// 2-byte loads for Q5_0 blocks (22 bytes) +// 4-byte loads for Q5_1 blocks (24 bytes) i32vec2 repack(uint ib, uint iqs) { - // Use 2-byte loads since a q5_0 block (22 bytes) is not divisible by 4 - const u16vec2 quants = u16vec2(data_a[ib].qs[iqs * 2 ], - data_a[ib].qs[iqs * 2 + 1]); + const u16vec2 quants = u16vec2(data_a_packed16[ib].qs[iqs * 2 ], + data_a_packed16[ib].qs[iqs * 2 + 1]); const uint32_t vui = pack32(quants); - const int32_t qh = int32_t((uint32_t(data_a[ib].qh[1]) << 16 | data_a[ib].qh[0]) >> (4 * iqs)); +#ifdef DATA_A_Q5_0 + const int32_t qh = int32_t((uint32_t(data_a_packed16[ib].qh[1]) << 16 | data_a_packed16[ib].qh[0]) >> (4 * iqs)); +#else // DATA_A_Q5_1 + const int32_t qh = int32_t(data_a_packed32[ib].qh >> (4 * iqs)); +#endif const int32_t v0 = int32_t(vui & 0x0F0F0F0F) | ((qh & 0xF) * 0x02040810) & 0x10101010; // (0,1,2,3) -> (4,12,20,28) @@ -50,40 +98,457 @@ i32vec2 repack(uint ib, uint iqs) { return i32vec2(v0, v1); } +#ifdef DATA_A_Q5_0 ACC_TYPE mul_q8_1(const int32_t q_sum, const float da, const vec2 dsb, const int32_t sum_divisor) { return ACC_TYPE(da * (float(q_sum) * dsb.x - (16 / sum_divisor) * dsb.y)); } -#endif - -#if defined(DATA_A_Q5_1) -i32vec2 repack(uint ib, uint iqs) { - // Use 4-byte loads since a q5_1 block (24 bytes) is divisible by 4 - const uint32_t vui = data_a_packed32[ib].qs[iqs]; - const int32_t qh = int32_t(data_a_packed32[ib].qh >> (4 * iqs)); - const int32_t v0 = int32_t(vui & 0x0F0F0F0F) - | ((qh & 0xF) * 0x02040810) & 0x10101010; // (0,1,2,3) -> (4,12,20,28) - - const int32_t v1 = int32_t((vui >> 4) & 0x0F0F0F0F) - | (((qh >> 16) & 0xF) * 0x02040810) & 0x10101010; // (16,17,18,19) -> (4,12,20,28) - - return i32vec2(v0, v1); -} - +#else // DATA_A_Q5_1 ACC_TYPE mul_q8_1(const int32_t q_sum, const vec2 dma, const vec2 dsb, const int32_t sum_divisor) { return ACC_TYPE(float(q_sum) * dma.x * dsb.x + dma.y * dsb.y / sum_divisor); } #endif +#ifdef MMQ_SHMEM +void block_a_to_shmem(const uint buf_ib, const uint ib, const uint iqs) { +#ifdef DATA_A_Q5_0 + buf_a[buf_ib].qs[iqs] = pack32(u16vec2(data_a_packed16[ib].qs[iqs * 2], + data_a_packed16[ib].qs[iqs * 2 + 1])); + + if (iqs == 0) { + buf_a[buf_ib].dm = FLOAT_TYPE(data_a_packed16[ib].d); + buf_a[buf_ib].qh = pack32(u16vec2(data_a_packed16[ib].qh[0], data_a_packed16[ib].qh[1])); + } +#else // DATA_A_Q5_1 + buf_a[buf_ib].qs[iqs] = data_a_packed32[ib].qs[iqs]; + + if (iqs == 0) { + buf_a[buf_ib].dm = FLOAT_TYPE_VEC2(data_a_packed32[ib].dm); + buf_a[buf_ib].qh = data_a_packed32[ib].qh; + } +#endif +} + +void block_a_to_registers(const uint reg_ib, const uint buf_ib) { + cache_a[reg_ib].dm = buf_a[buf_ib].dm; + cache_a[reg_ib].qh = buf_a[buf_ib].qh; + + [[unroll]] for (uint iqs = 0; iqs < 4; iqs++) { + cache_a[reg_ib].qs[iqs] = buf_a[buf_ib].qs[iqs]; + } +} + +ACC_TYPE mmq_dot_product(const uint ib_a) { + int32_t q_sum = 0; + [[unroll]] for (uint iqs = 0; iqs < 4; iqs++) { + const uint32_t vui = cache_a[ib_a].qs[iqs]; + const int32_t qh = int32_t(cache_a[ib_a].qh >> (4 * iqs)); + const int32_t qs_a0 = int32_t(vui & 0x0F0F0F0F) + | ((qh & 0xF) * 0x02040810) & 0x10101010; // (0,1,2,3) -> (4,12,20,28) + const int32_t qs_a1 = int32_t((vui >> 4) & 0x0F0F0F0F) + | (((qh >> 16) & 0xF) * 0x02040810) & 0x10101010; // (16,17,18,19) -> (4,12,20,28) + + const int32_t qs_b0 = cache_b.qs[iqs]; + const int32_t qs_b1 = cache_b.qs[iqs + 4]; + + q_sum += dotPacked4x8EXT(qs_a0, qs_b0); + q_sum += dotPacked4x8EXT(qs_a1, qs_b1); + } + + return mul_q8_1(q_sum, cache_a[ib_a].dm, cache_b.ds, 1); +} +#endif // MMQ_SHMEM +#endif + #if defined(DATA_A_Q8_0) +// 2-byte loads for Q8_0 blocks (34 bytes) int32_t repack(uint ib, uint iqs) { - // Use 2-byte loads since a q8_0 block (34 bytes) is not divisible by 4 - return pack32(i16vec2(data_a[ib].qs[iqs * 2 ], - data_a[ib].qs[iqs * 2 + 1])); + return pack32(i16vec2(data_a_packed16[ib].qs[iqs * 2 ], + data_a_packed16[ib].qs[iqs * 2 + 1])); } ACC_TYPE mul_q8_1(const int32_t q_sum, const float da, const vec2 dsb, const int32_t sum_divisor) { return ACC_TYPE(float(q_sum) * da * dsb.x); } + +#ifdef MMQ_SHMEM +void block_a_to_shmem(const uint buf_ib, const uint ib, const uint iqs) { + buf_a[buf_ib].qs[iqs] = pack32(i16vec2(data_a_packed16[ib].qs[iqs * 2], + data_a_packed16[ib].qs[iqs * 2 + 1])); + + if (iqs == 0) { + buf_a[buf_ib].dm = FLOAT_TYPE(data_a_packed16[ib].d); + } +} + +void block_a_to_registers(const uint reg_ib, const uint buf_ib) { + cache_a[reg_ib].dm = buf_a[buf_ib].dm; + + [[unroll]] for (uint iqs = 0; iqs < 8; iqs++) { + cache_a[reg_ib].qs[iqs] = buf_a[buf_ib].qs[iqs]; + } +} + +ACC_TYPE mmq_dot_product(const uint ib_a) { + int32_t q_sum = 0; + [[unroll]] for (uint iqs = 0; iqs < 8; iqs++) { + const int32_t qs_a = cache_a[ib_a].qs[iqs]; + const int32_t qs_b = cache_b.qs[iqs]; + + q_sum += dotPacked4x8EXT(qs_a, qs_b); + } + + return mul_q8_1(q_sum, cache_a[ib_a].dm, cache_b.ds, 1); +} +#endif // MMQ_SHMEM +#endif + +#if defined(DATA_A_MXFP4) +// 1-byte loads for mxfp4 blocks (17 bytes) +i32vec2 repack(uint ib, uint iqs) { + const uint32_t quants = pack32(u8vec4(data_a[ib].qs[iqs * 4 ], + data_a[ib].qs[iqs * 4 + 1], + data_a[ib].qs[iqs * 4 + 2], + data_a[ib].qs[iqs * 4 + 3])); + + return i32vec2( quants & 0x0F0F0F0F, + (quants >> 4) & 0x0F0F0F0F); +} + +ACC_TYPE mul_q8_1(const int32_t q_sum, const float da, const vec2 dsb, const int32_t sum_divisor) { + return ACC_TYPE(da * dsb.x * float(q_sum)); +} + +#ifdef MMQ_SHMEM +void block_a_to_shmem(const uint buf_ib, const uint ib, const uint iqs) { + const uint32_t qs = pack32(u8vec4(data_a[ib].qs[iqs * 4 ], + data_a[ib].qs[iqs * 4 + 1], + data_a[ib].qs[iqs * 4 + 2], + data_a[ib].qs[iqs * 4 + 3])); + + const u8vec4 i_a0 = unpack8( qs & 0x0F0F0F0F); + const u8vec4 i_a1 = unpack8((qs >> 4) & 0x0F0F0F0F); + + buf_a[buf_ib].qs[iqs ] = pack32(i8vec4(kvalues_mxfp4[i_a0.x], kvalues_mxfp4[i_a0.y], kvalues_mxfp4[i_a0.z], kvalues_mxfp4[i_a0.w])); + buf_a[buf_ib].qs[iqs + 4] = pack32(i8vec4(kvalues_mxfp4[i_a1.x], kvalues_mxfp4[i_a1.y], kvalues_mxfp4[i_a1.z], kvalues_mxfp4[i_a1.w])); + + if (iqs == 0) { + buf_a[buf_ib].d = FLOAT_TYPE(e8m0_to_fp32(data_a[ib].e) * 0.5); + } +} + +void block_a_to_registers(const uint reg_ib, const uint buf_ib) { + cache_a[reg_ib].d = buf_a[buf_ib].d; + + [[unroll]] for (uint iqs = 0; iqs < 8; iqs++) { + cache_a[reg_ib].qs[iqs] = buf_a[buf_ib].qs[iqs]; + } +} + +ACC_TYPE mmq_dot_product(const uint ib_a) { + int32_t q_sum = 0; + [[unroll]] for (uint iqs = 0; iqs < 8; iqs++) { + const int32_t qs_a = cache_a[ib_a].qs[iqs]; + + q_sum += dotPacked4x8EXT(qs_a, cache_b.qs[iqs]); + } + + return mul_q8_1(q_sum, cache_a[ib_a].d, cache_b.ds, 1); +} +#endif // MMQ_SHMEM +#endif + +// For k-quants, ib and iqs still assume 32-wide blocks, but k-quants are 256-wide +// iqs still refers to a 32-bit integer, meaning 0..7 for 32-wide quants +#if defined(DATA_A_Q2_K) +// 4-byte loads for Q2_K blocks (84 bytes) +int32_t repack(uint ib, uint iqs) { + const uint ib_k = ib / 8; + const uint iqs_k = (ib % 8) * 8 + iqs; + + const uint qs_idx = (iqs_k / 32) * 8 + (iqs_k % 8); + const uint qs_shift = ((iqs_k % 32) / 8) * 2; + + return int32_t((data_a_packed32[ib_k].qs[qs_idx] >> qs_shift) & 0x03030303); +} + +uint8_t get_scale(uint ib, uint iqs) { + const uint ib_k = ib / 8; + const uint iqs_k = (ib % 8) * 8 + iqs; + + return data_a[ib_k].scales[iqs_k / 4]; +} + +ACC_TYPE mul_q8_1(const int32_t sum_d, const int32_t sum_m, const vec2 dma, const vec2 dsb, const int32_t sum_divisor) { + return ACC_TYPE(dsb.x * (dma.x * float(sum_d) - dma.y * float(sum_m))); +} + +#ifdef MMQ_SHMEM +void block_a_to_shmem(const uint buf_ib, const uint ib, const uint iqs) { + const uint ib_k = ib / 8; + const uint iqs_k = (ib % 8) * 8 + iqs * QUANT_R_MMQ; + + const uint qs_idx = (iqs_k / 32) * 8 + (iqs_k % 8); + const uint qs_shift = ((iqs_k % 32) / 8) * 2; + + // Repack 4x4 quants into one int + const uint32_t vals0 = (data_a_packed32[ib_k].qs[qs_idx ] >> qs_shift) & 0x03030303; + const uint32_t vals1 = (data_a_packed32[ib_k].qs[qs_idx + 1] >> qs_shift) & 0x03030303; + const uint32_t vals2 = (data_a_packed32[ib_k].qs[qs_idx + 2] >> qs_shift) & 0x03030303; + const uint32_t vals3 = (data_a_packed32[ib_k].qs[qs_idx + 3] >> qs_shift) & 0x03030303; + + buf_a[buf_ib].qs[iqs] = vals0 | (vals1 << 2) | (vals2 << 4) | (vals3 << 6); + + if (iqs == 0) { + buf_a[buf_ib].dm = FLOAT_TYPE_VEC2(data_a_packed32[ib_k].dm); + buf_a[buf_ib].scales = unpack8(data_a_packed16[ib_k].scales[iqs_k / 8]); + } +} + +void block_a_to_registers(const uint reg_ib, const uint buf_ib) { + cache_a[reg_ib].dm = buf_a[buf_ib].dm; + cache_a[reg_ib].scales = buf_a[buf_ib].scales; + + [[unroll]] for (uint iqs = 0; iqs < 2; iqs++) { + cache_a[reg_ib].qs[iqs] = buf_a[buf_ib].qs[iqs]; + } +} + +ACC_TYPE mmq_dot_product(const uint ib_a) { + int32_t sum_d = 0; + int32_t sum_m = 0; + + [[unroll]] for (uint iqs = 0; iqs < 8; iqs++) { + const uint8_t scale = cache_a[ib_a].scales[iqs / 4]; + const int32_t scale_m = int32_t(scale >> 4) * 0x01010101; // Duplicate 8-bit value across 32-bits. + const int32_t qs_a = int32_t((cache_a[ib_a].qs[iqs / 4] >> ((iqs % 4) * 2)) & 0x03030303); + + sum_d += dotPacked4x8EXT(qs_a, cache_b.qs[iqs]) * (scale & 0xF); + sum_m += dotPacked4x8EXT(scale_m, cache_b.qs[iqs]); + } + + return mul_q8_1(sum_d, sum_m, cache_a[ib_a].dm, cache_b.ds, 1); +} +#endif // MMQ_SHMEM +#endif + +#if defined(DATA_A_Q3_K) +// 2-byte loads for Q3_K blocks (110 bytes) +#ifdef MMQ_SHMEM +void block_a_to_shmem(const uint buf_ib, const uint ib, const uint iqs) { + const uint ib_k = ib / 8; + const uint hm_idx = iqs * QUANT_R_MMQ; + const uint iqs_k = (ib % 8) * 8 + hm_idx; + + const uint qs_idx = (iqs_k / 32) * 8 + (iqs_k % 8); + const uint qs_shift = ((iqs_k % 32) / 8) * 2; + const uint hm_shift = iqs_k / 8; + + // Repack 2x4 quants into one int + // Add the 3rd bit instead of subtracting it to allow packing the quants + const i8vec2 vals00 = unpack8(int16_t((data_a_packed16[ib_k].qs[qs_idx * 2 ] >> qs_shift) & uint16_t(0x0303))) | + unpack8(int16_t(((data_a_packed16[ib_k].hmask[hm_idx * 2 ] >> hm_shift) & uint16_t(0x0101)) << 2)); + const i8vec2 vals01 = unpack8(int16_t((data_a_packed16[ib_k].qs[qs_idx * 2 + 1 ] >> qs_shift) & uint16_t(0x0303))) | + unpack8(int16_t(((data_a_packed16[ib_k].hmask[hm_idx * 2 + 1] >> hm_shift) & uint16_t(0x0101)) << 2)); + const i8vec2 vals10 = unpack8(int16_t((data_a_packed16[ib_k].qs[qs_idx * 2 + 2 ] >> qs_shift) & uint16_t(0x0303))) | + unpack8(int16_t(((data_a_packed16[ib_k].hmask[hm_idx * 2 + 2] >> hm_shift) & uint16_t(0x0101)) << 2)); + const i8vec2 vals11 = unpack8(int16_t((data_a_packed16[ib_k].qs[qs_idx * 2 + 3 ] >> qs_shift) & uint16_t(0x0303))) | + unpack8(int16_t(((data_a_packed16[ib_k].hmask[hm_idx * 2 + 3] >> hm_shift) & uint16_t(0x0101)) << 2)); + buf_a[buf_ib].qs[iqs] = pack32(u8vec4(vals00.x, vals00.y, vals01.x, vals01.y)) | + (pack32(u8vec4(vals10.x, vals10.y, vals11.x, vals11.y)) << 4); + + if (iqs == 0) { + const uint is = iqs_k / 4; + const i8vec2 scales = i8vec2(unpack8(((data_a_packed16[ib_k].scales[(is % 8 ) / 2] >> (4 * (is / 8))) & 0x0F0F) | + (((data_a_packed16[ib_k].scales[(8 + (is % 4)) / 2] >> (2 * (is / 4))) & 0x0303) << 4))); + + buf_a[buf_ib].d_scales = FLOAT_TYPE(data_a_packed16[ib_k].d) * FLOAT_TYPE_VEC2(scales - 32); + } +} + +void block_a_to_registers(const uint reg_ib, const uint buf_ib) { + cache_a[reg_ib].d_scales = buf_a[buf_ib].d_scales; + + [[unroll]] for (uint iqs = 0; iqs < 4; iqs++) { + cache_a[reg_ib].qs[iqs] = buf_a[buf_ib].qs[iqs]; + } +} + +ACC_TYPE mmq_dot_product(const uint ib_a) { + float result = 0.0; + int32_t q_sum = 0; + + [[unroll]] for (uint iqs = 0; iqs < 4; iqs++) { + // Subtract 4 from the quants to correct the 3rd bit offset + const int32_t qs_a = pack32(unpack8(int32_t((cache_a[ib_a].qs[iqs / 2] >> ((iqs % 2) * 4)) & 0x0F0F0F0F)) - int8_t(4)); + + q_sum += dotPacked4x8EXT(qs_a, cache_b.qs[iqs]); + } + result += float(cache_a[ib_a].d_scales[0]) * float(q_sum); + q_sum = 0; + + [[unroll]] for (uint iqs = 4; iqs < 8; iqs++) { + const int32_t qs_a = pack32(unpack8(int32_t((cache_a[ib_a].qs[iqs / 2] >> ((iqs % 2) * 4)) & 0x0F0F0F0F)) - int8_t(4)); + + q_sum += dotPacked4x8EXT(qs_a, cache_b.qs[iqs]); + } + result += float(cache_a[ib_a].d_scales[1]) * float(q_sum); + + return ACC_TYPE(cache_b.ds.x * result); +} +#endif // MMQ_SHMEM +#endif + +#if defined(DATA_A_Q4_K) || defined(DATA_A_Q5_K) +// 4-byte loads for Q4_K blocks (144 bytes) and Q5_K blocks (176 bytes) +ACC_TYPE mul_q8_1(const int32_t q_sum, const vec2 dma, const vec2 dsb, const int32_t sum_divisor) { + return ACC_TYPE(dsb.x * dma.x * float(q_sum) - dma.y * dsb.y); +} + +#ifdef MMQ_SHMEM +void block_a_to_shmem(const uint buf_ib, const uint ib, const uint iqs) { + const uint ib_k = ib / 8; + const uint iqs_k = (ib % 8) * 8 + iqs * QUANT_R_MMQ; + + const uint qs_idx = (iqs_k / 16) * 8 + (iqs_k % 8); + const uint qs_shift = ((iqs_k % 16) / 8) * 4; + + // Repack 2x4 quants into one int +#if defined(DATA_A_Q4_K) + const uint32_t vals0 = (data_a_packed32[ib_k].qs[qs_idx ] >> qs_shift) & 0x0F0F0F0F; + const uint32_t vals1 = (data_a_packed32[ib_k].qs[qs_idx + 1] >> qs_shift) & 0x0F0F0F0F; + + buf_a[buf_ib].qs[iqs] = vals0 | (vals1 << 4); +#else // defined(DATA_A_Q5_K) + const uint qh_idx = iqs * QUANT_R_MMQ; + const uint qh_shift = iqs_k / 8; + + buf_a[buf_ib].qs[iqs] = int32_t(((data_a_packed32[ib_k].qs[qs_idx] >> qs_shift) & 0x0F0F0F0F) | + (((data_a_packed32[ib_k].qh[qh_idx] >> qh_shift) & 0x01010101) << 4)); +#endif + + + if (iqs == 0) { + // Scale index + const uint is = iqs_k / 8; + u8vec2 scale_dm; + if (is < 4) { + scale_dm = u8vec2(data_a[ib_k].scales[is] & 0x3F, data_a[ib_k].scales[is + 4] & 0x3F); + } else { + scale_dm = u8vec2((data_a[ib_k].scales[is+4] & 0xF) | ((data_a[ib_k].scales[is-4] & 0xC0) >> 2), + (data_a[ib_k].scales[is+4] >> 4) | ((data_a[ib_k].scales[is ] & 0xC0) >> 2)); + } + + buf_a[buf_ib].dm = FLOAT_TYPE_VEC2(data_a_packed32[ib_k].dm) * FLOAT_TYPE_VEC2(scale_dm); + } +} + +void block_a_to_registers(const uint reg_ib, const uint buf_ib) { + cache_a[reg_ib].dm = buf_a[buf_ib].dm; + + [[unroll]] for (uint iqs = 0; iqs < 8 / QUANT_R_MMQ; iqs++) { + cache_a[reg_ib].qs[iqs] = buf_a[buf_ib].qs[iqs]; + } +} + +ACC_TYPE mmq_dot_product(const uint ib_a) { + int32_t q_sum = 0; + + [[unroll]] for (uint iqs = 0; iqs < 8; iqs++) { +#if defined(DATA_A_Q4_K) + const int32_t qs_a = int32_t((cache_a[ib_a].qs[iqs / 2] >> ((iqs % 2) * 4)) & 0x0F0F0F0F); +#else // defined(DATA_A_Q5_K) + const int32_t qs_a = cache_a[ib_a].qs[iqs]; +#endif + + q_sum += dotPacked4x8EXT(qs_a, cache_b.qs[iqs]); + } + + return mul_q8_1(q_sum, cache_a[ib_a].dm, cache_b.ds, 1); +} +#endif // MMQ_SHMEM +#endif + +#ifdef MMQ_SHMEM +void block_b_to_shmem(const uint buf_ib, const uint ib, const uint iqs) { + const uint ib_outer = ib / 4; + const uint ib_inner = ib % 4; + + if (iqs == 0) { + buf_b[buf_ib].ds = FLOAT_TYPE_VEC2(data_b[ib_outer].ds[ib_inner]); + } + + const ivec4 values = data_b[ib_outer].qs[ib_inner * 2 + iqs]; + buf_b[buf_ib].qs[iqs * 4 ] = values.x; + buf_b[buf_ib].qs[iqs * 4 + 1] = values.y; + buf_b[buf_ib].qs[iqs * 4 + 2] = values.z; + buf_b[buf_ib].qs[iqs * 4 + 3] = values.w; +} + +void block_b_to_registers(const uint ib) { + cache_b.ds = buf_b[ib].ds; + [[unroll]] for (uint iqs = 0; iqs < BK / 4; iqs++) { + cache_b.qs[iqs] = buf_b[ib].qs[iqs]; + } +} +#endif + +#if defined(DATA_A_Q6_K) +// 2-byte loads for Q6_K blocks (210 bytes) +#ifdef MMQ_SHMEM +void block_a_to_shmem(const uint buf_ib, const uint ib, const uint iqs) { + const uint ib_k = ib / 8; + const uint iqs_k = (ib % 8) * 8 + iqs; + + const uint ql_idx = (iqs_k / 32) * 16 + iqs_k % 16; + const uint ql_shift = ((iqs_k % 32) / 16) * 4; + + const uint qh_idx = (iqs_k / 32) * 8 + iqs; + const uint qh_shift = ((iqs_k % 32) / 8) * 2; + + const i8vec2 vals00 = (unpack8(int16_t((data_a_packed16[ib_k].ql[ql_idx * 2 ] >> ql_shift) & uint16_t(0x0F0F))) | + unpack8(int16_t(((data_a_packed16[ib_k].qh[qh_idx * 2 ] >> qh_shift) & uint16_t(0x0303)) << 4))) - int8_t(32); + const i8vec2 vals01 = (unpack8(int16_t((data_a_packed16[ib_k].ql[ql_idx * 2 + 1] >> ql_shift) & uint16_t(0x0F0F))) | + unpack8(int16_t(((data_a_packed16[ib_k].qh[qh_idx * 2 + 1] >> qh_shift) & uint16_t(0x0303)) << 4))) - int8_t(32); + buf_a[buf_ib].qs[iqs] = pack32(i8vec4(vals00.x, vals00.y, vals01.x, vals01.y)); + + if (iqs == 0) { + const uint is = iqs_k / 4; + const i8vec2 scales = unpack8(data_a_packed16[ib_k].scales[is / 2]); + + buf_a[buf_ib].d_scales = FLOAT_TYPE(data_a_packed16[ib_k].d) * FLOAT_TYPE_VEC2(scales); + } +} + +void block_a_to_registers(const uint reg_ib, const uint buf_ib) { + cache_a[reg_ib].d_scales = buf_a[buf_ib].d_scales; + + [[unroll]] for (uint iqs = 0; iqs < 8; iqs++) { + cache_a[reg_ib].qs[iqs] = buf_a[buf_ib].qs[iqs]; + } +} + +ACC_TYPE mmq_dot_product(const uint ib_a) { + float result = 0.0; + int32_t q_sum = 0; + + [[unroll]] for (uint iqs = 0; iqs < 4; iqs++) { + const int32_t qs_a = cache_a[ib_a].qs[iqs]; + + q_sum += dotPacked4x8EXT(qs_a, cache_b.qs[iqs]); + } + result += float(cache_a[ib_a].d_scales[0]) * float(q_sum); + q_sum = 0; + + [[unroll]] for (uint iqs = 4; iqs < 8; iqs++) { + const int32_t qs_a = cache_a[ib_a].qs[iqs]; + + q_sum += dotPacked4x8EXT(qs_a, cache_b.qs[iqs]); + } + result += float(cache_a[ib_a].d_scales[1]) * float(q_sum); + + return ACC_TYPE(cache_b.ds.x * result); +} +#endif // MMQ_SHMEM #endif #if defined(DATA_A_Q4_0) || defined(DATA_A_Q5_0) || defined(DATA_A_Q8_0) || defined(DATA_A_IQ1_S) || defined(DATA_A_IQ2_XXS) || defined(DATA_A_IQ2_XS) || defined(DATA_A_IQ2_S) || defined(DATA_A_IQ3_XXS) || defined(DATA_A_IQ3_S) || defined(DATA_A_IQ4_XS) || defined(DATA_A_IQ4_NL) @@ -103,3 +568,10 @@ FLOAT_TYPE_VEC2 get_dm(uint ib) { return FLOAT_TYPE_VEC2(data_a_packed32[ib].dm); } #endif + +#if defined(DATA_A_Q2_K) +FLOAT_TYPE_VEC2 get_dm(uint ib) { + const uint ib_k = ib / 8; + return FLOAT_TYPE_VEC2(data_a_packed32[ib_k].dm); +} +#endif diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq_shmem_types.glsl b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq_shmem_types.glsl new file mode 100644 index 00000000..1c0f5306 --- /dev/null +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/mul_mmq_shmem_types.glsl @@ -0,0 +1,78 @@ +#if defined(DATA_A_Q4_0) +#define QUANT_R_MMQ 2 +struct block_a_cache { + uint32_t qs[16/4]; + FLOAT_TYPE dm; +}; +#elif defined(DATA_A_Q4_1) +#define QUANT_R_MMQ 2 +struct block_a_cache { + uint32_t qs[16/4]; + FLOAT_TYPE_VEC2 dm; +}; +#elif defined(DATA_A_Q5_0) +#define QUANT_R_MMQ 2 +struct block_a_cache { + uint32_t qs[16/4]; + uint32_t qh; + FLOAT_TYPE dm; +}; +#elif defined(DATA_A_Q5_1) +#define QUANT_R_MMQ 2 +struct block_a_cache { + uint32_t qs[16/4]; + uint32_t qh; + FLOAT_TYPE_VEC2 dm; +}; +#elif defined(DATA_A_Q8_0) +#define QUANT_R_MMQ 1 +// AMD likes 4, Intel likes 1 and Nvidia likes 2 +// #define BK_STEP 1 +struct block_a_cache { + int32_t qs[32/4]; + FLOAT_TYPE dm; +}; +#elif defined(DATA_A_MXFP4) +#define QUANT_R_MMQ 2 +struct block_a_cache { + int32_t qs[8]; + FLOAT_TYPE d; +}; +#elif defined(DATA_A_Q2_K) +#define QUANT_R_MMQ 4 +struct block_a_cache { + uint32_t qs[2]; + u8vec2 scales; + FLOAT_TYPE_VEC2 dm; +}; +#elif defined(DATA_A_Q3_K) +#define QUANT_R_MMQ 2 +struct block_a_cache { + uint32_t qs[4]; + FLOAT_TYPE_VEC2 d_scales; +}; +#elif defined(DATA_A_Q4_K) +#define QUANT_R_MMQ 2 +struct block_a_cache { + uint32_t qs[4]; + FLOAT_TYPE_VEC2 dm; +}; +#elif defined(DATA_A_Q5_K) +#define QUANT_R_MMQ 1 +struct block_a_cache { + int32_t qs[8]; + FLOAT_TYPE_VEC2 dm; +}; +#elif defined(DATA_A_Q6_K) +#define QUANT_R_MMQ 1 +struct block_a_cache { + int32_t qs[8]; + FLOAT_TYPE_VEC2 d_scales; +}; +#endif + +struct block_b_cache +{ + int32_t qs[8]; + FLOAT_TYPE_VEC2 ds; +}; diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/rope_head.glsl b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/rope_head.glsl index 50fc1f1e..0eda186c 100644 --- a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/rope_head.glsl +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/rope_head.glsl @@ -10,6 +10,7 @@ layout (binding = 0) readonly buffer X {A_TYPE data_a[];}; layout (binding = 1) readonly buffer Y {int data_pos[];}; layout (binding = 2) readonly buffer Z {float data_ff[];}; layout (binding = 3) writeonly buffer D {D_TYPE data_d[];}; +layout (binding = 4) readonly buffer I {uvec2 data_i[];}; // indices for set_rows layout (push_constant) uniform parameter { uint ncols; @@ -27,6 +28,7 @@ layout (push_constant) uniform parameter { uint s2; int sections[4]; uint is_back; + uint set_rows_stride; } p; float rope_yarn_ramp(const float low, const float high, const uint i0) { diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/rope_neox.comp b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/rope_neox.comp index 06e095be..9f453815 100644 --- a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/rope_neox.comp +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/rope_neox.comp @@ -16,12 +16,19 @@ void main() { const uint row_x = row_dst % ne1; const uint channel_x = row_dst / ne1; - const uint idst = row_dst*ne0 + i0/2; + uint idst = row_dst*ne0 + i0/2; const uint ix = channel_x*p.s2 + row_x*p.s1 + i0/2; + // Fusion optimization: ROPE + VIEW + SET_ROWS.. + // The rope output is viewed as a 1D tensor and offset based on a row index in data_i. + if (p.set_rows_stride != 0) { + idst = row_x*ne0 + i0/2; + idst += data_i[channel_x].x * p.set_rows_stride; + } + if (i0 >= p.n_dims) { - data_d[idst + i0/2 + 0] = data_a[ix + i0/2 + 0]; - data_d[idst + i0/2 + 1] = data_a[ix + i0/2 + 1]; + data_d[idst + i0/2 + 0] = D_TYPE(data_a[ix + i0/2 + 0]); + data_d[idst + i0/2 + 1] = D_TYPE(data_a[ix + i0/2 + 1]); return; } diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/rope_norm.comp b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/rope_norm.comp index 6ba95754..f4209ed9 100644 --- a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/rope_norm.comp +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/rope_norm.comp @@ -16,12 +16,19 @@ void main() { const uint row_x = row_dst % ne1; const uint channel_x = row_dst / ne1; - const uint idst = row_dst*ne0 + i0; + uint idst = row_dst*ne0 + i0; const uint ix = channel_x*p.s2 + row_x*p.s1 + i0; + // Fusion optimization: ROPE + VIEW + SET_ROWS.. + // The rope output is viewed as a 1D tensor and offset based on a row index in data_i. + if (p.set_rows_stride != 0) { + idst = row_x*ne0 + i0; + idst += data_i[channel_x].x * p.set_rows_stride; + } + if (i0 >= p.n_dims) { - data_d[idst + 0] = data_a[ix + 0]; - data_d[idst + 1] = data_a[ix + 1]; + data_d[idst + 0] = D_TYPE(data_a[ix + 0]); + data_d[idst + 1] = D_TYPE(data_a[ix + 1]); return; } diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/topk_moe.comp b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/topk_moe.comp index 9e56d5f8..bc1c278b 100644 --- a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/topk_moe.comp +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/topk_moe.comp @@ -11,6 +11,8 @@ layout (push_constant) uniform parameter { uint n_rows; uint n_expert_used; + float clamp_min; + float clamp_max; }; layout(local_size_x_id = 0, local_size_y = 4, local_size_z = 1) in; @@ -18,6 +20,7 @@ layout(local_size_x_id = 0, local_size_y = 4, local_size_z = 1) in; layout(constant_id = 0) const uint WARP_SIZE = 32; layout(constant_id = 1) const uint n_experts = 512; layout(constant_id = 2) const bool with_norm = true; +layout(constant_id = 3) const bool late_softmax = false; const uint experts_per_thread = (n_experts > WARP_SIZE) ? n_experts / WARP_SIZE : 1; @@ -25,6 +28,52 @@ layout (binding = 0, std430) readonly buffer Logits {float logits[];}; layout (binding = 1, std430) writeonly buffer Weights {float weights[];}; layout (binding = 2, std430) writeonly buffer Ids {uint ids[];}; +const float INFINITY = 1.0 / 0.0; + +// Warp-local softmax used for both the pre-top-k logits and the post-top-k delayed path. +void softmax_warp_inplace(inout float vals[experts_per_thread], const uint limit, const uint lane, const bool use_limit) { + float max_val = -INFINITY; + + [[unroll]] + for (int i = 0; i < experts_per_thread; i++) { + const uint idx = lane + i * WARP_SIZE; + const bool is_active = !use_limit || (idx < limit); + if (is_active) { + max_val = max(max_val, vals[i]); + } + } + + max_val = subgroupMax(max_val); + + float sum = 0.f; + + [[unroll]] + for (int i = 0; i < experts_per_thread; i++) { + const uint idx = lane + i * WARP_SIZE; + const bool is_active = !use_limit || (idx < limit); + if (is_active) { + const float val = exp(vals[i] - max_val); + vals[i] = val; + sum += val; + } else { + vals[i] = 0.f; + } + } + + sum = subgroupAdd(sum); + + const float inv_sum = 1.0f / sum; + + [[unroll]] + for (int i = 0; i < experts_per_thread; i++) { + const uint idx = lane + i * WARP_SIZE; + const bool is_active = !use_limit || (idx < limit); + if (is_active) { + vals[i] *= inv_sum; + } + } +} + void main() { const uint row = gl_WorkGroupID.x * gl_WorkGroupSize.y + gl_LocalInvocationID.y; if (row >= n_rows) { @@ -35,43 +84,16 @@ void main() { const uint weights_offset = n_expert_used * row; const uint ids_offset = n_experts * row; - float logits_r[experts_per_thread]; - - const float INFINITY = 1.0 / 0.0; + float wt[experts_per_thread]; [[unroll]] for (uint i = 0; i < n_experts; i += WARP_SIZE) { - const uint expert = i + gl_LocalInvocationID.x; - logits_r[i / WARP_SIZE] = n_experts % WARP_SIZE == 0 || expert < n_experts ? logits[logits_offset + expert] : -INFINITY; + const uint expert = i + gl_LocalInvocationID.x; + wt[i / WARP_SIZE] = (n_experts % WARP_SIZE == 0 || expert < n_experts) ? logits[logits_offset + expert] : -INFINITY; } - float max_val = logits_r[0]; - - [[unroll]] - for (int i = 1; i < experts_per_thread; i++) { - const float val = logits_r[i]; - max_val = max(val, max_val); - } - - max_val = subgroupMax(max_val); - - float wt[experts_per_thread]; - float tmp = 0.f; - - [[unroll]] - for (int i = 0; i < experts_per_thread; i++) { - const float val = logits_r[i]; - wt[i] = exp(val - max_val); - tmp += wt[i]; - } - - tmp = subgroupAdd(tmp); - - const float inv_sum = 1.0f / tmp; - - [[unroll]] - for (int i = 0; i < experts_per_thread; i++) { - wt[i] = wt[i] * inv_sum; + if (!late_softmax) { + softmax_warp_inplace(wt, n_experts, gl_LocalInvocationID.x, false); } // at this point, each thread holds a portion of softmax, @@ -82,6 +104,11 @@ void main() { float output_weights[experts_per_thread]; + [[unroll]] + for (int i = 0; i < experts_per_thread; i++) { + output_weights[i] = 0.f; + } + for (int k = 0; k < n_expert_used; k++) { float max_val = wt[0]; uint max_expert = gl_LocalInvocationID.x; @@ -121,6 +148,7 @@ void main() { if (with_norm) { wt_sum = subgroupAdd(wt_sum); + wt_sum = clamp(wt_sum, clamp_min, clamp_max); const float inv_sum = 1.0f / wt_sum; [[unroll]] @@ -129,6 +157,10 @@ void main() { } } + if (late_softmax) { + softmax_warp_inplace(output_weights, n_expert_used, gl_LocalInvocationID.x, true); + } + [[unroll]] for (uint i = 0; i < experts_per_thread; ++i) { uint idx = i * WARP_SIZE + gl_LocalInvocationID.x; diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/types.glsl b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/types.glsl index 2fa54ce5..02578c77 100644 --- a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/types.glsl +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/types.glsl @@ -66,6 +66,7 @@ struct block_q4_0_packed16 #define QUANT_AUXF 1 #define A_TYPE block_q4_0 #define A_TYPE_PACKED16 block_q4_0_packed16 +#define DATA_A_QUANT_LEGACY #endif #define QUANT_K_Q4_1 32 @@ -98,6 +99,7 @@ struct block_q4_1_packed32 #define A_TYPE block_q4_1 #define A_TYPE_PACKED16 block_q4_1_packed16 #define A_TYPE_PACKED32 block_q4_1_packed32 +#define DATA_A_QUANT_LEGACY #endif #define QUANT_K_Q5_0 32 @@ -123,6 +125,7 @@ struct block_q5_0_packed16 #define QUANT_AUXF 1 #define A_TYPE block_q5_0 #define A_TYPE_PACKED16 block_q5_0_packed16 +#define DATA_A_QUANT_LEGACY #endif #define QUANT_K_Q5_1 32 @@ -158,6 +161,7 @@ struct block_q5_1_packed32 #define A_TYPE block_q5_1 #define A_TYPE_PACKED16 block_q5_1_packed16 #define A_TYPE_PACKED32 block_q5_1_packed32 +#define DATA_A_QUANT_LEGACY #endif #define QUANT_K_Q8_0 32 @@ -186,6 +190,7 @@ struct block_q8_0_packed32 #define A_TYPE block_q8_0 #define A_TYPE_PACKED16 block_q8_0_packed16 #define A_TYPE_PACKED32 block_q8_0_packed32 +#define DATA_A_QUANT_LEGACY #endif #define QUANT_K_Q8_1 32 @@ -226,21 +231,21 @@ struct block_q2_K { uint8_t scales[QUANT_K_Q2_K/16]; uint8_t qs[QUANT_K_Q2_K/4]; - f16vec2 d; + f16vec2 dm; }; struct block_q2_K_packed16 { uint16_t scales[QUANT_K_Q2_K/16/2]; uint16_t qs[QUANT_K_Q2_K/4/2]; - f16vec2 d; + f16vec2 dm; }; struct block_q2_K_packed32 { uint32_t scales[QUANT_K_Q2_K/16/4]; uint32_t qs[QUANT_K_Q2_K/4/4]; - f16vec2 d; + f16vec2 dm; }; #if defined(DATA_A_Q2_K) @@ -249,6 +254,8 @@ struct block_q2_K_packed32 #define A_TYPE block_q2_K #define A_TYPE_PACKED16 block_q2_K_packed16 #define A_TYPE_PACKED32 block_q2_K_packed32 +#define SCALES_PER_32 2 +#define DATA_A_QUANT_K #endif #define QUANT_K_Q3_K 256 @@ -274,27 +281,28 @@ struct block_q3_K_packed16 #define QUANT_R 1 #define A_TYPE block_q3_K #define A_TYPE_PACKED16 block_q3_K_packed16 +#define DATA_A_QUANT_K #endif #define QUANT_K_Q4_K 256 struct block_q4_K { - f16vec2 d; + f16vec2 dm; uint8_t scales[3*QUANT_K_Q4_K/64]; uint8_t qs[QUANT_K_Q4_K/2]; }; struct block_q4_K_packed16 { - f16vec2 d; + f16vec2 dm; uint16_t scales[3*QUANT_K_Q4_K/64/2]; uint16_t qs[QUANT_K_Q4_K/2/2]; }; struct block_q4_K_packed32 { - f16vec2 d; + f16vec2 dm; uint32_t scales[3*QUANT_K_Q4_K/64/4]; uint32_t qs[QUANT_K_Q4_K/2/4]; }; @@ -310,13 +318,14 @@ struct block_q4_K_packed128 #define A_TYPE block_q4_K #define A_TYPE_PACKED16 block_q4_K_packed16 #define A_TYPE_PACKED32 block_q4_K_packed32 +#define DATA_A_QUANT_K #endif #define QUANT_K_Q5_K 256 struct block_q5_K { - f16vec2 d; + f16vec2 dm; uint8_t scales[12]; uint8_t qh[QUANT_K_Q5_K/8]; uint8_t qs[QUANT_K_Q5_K/2]; @@ -324,12 +333,20 @@ struct block_q5_K struct block_q5_K_packed16 { - f16vec2 d; + f16vec2 dm; uint16_t scales[12/2]; uint16_t qh[QUANT_K_Q5_K/8/2]; uint16_t qs[QUANT_K_Q5_K/2/2]; }; +struct block_q5_K_packed32 +{ + f16vec2 dm; + uint32_t scales[12/4]; + uint32_t qh[QUANT_K_Q5_K/8/4]; + uint32_t qs[QUANT_K_Q5_K/2/4]; +}; + struct block_q5_K_packed128 { uvec4 q5k[11]; @@ -340,6 +357,8 @@ struct block_q5_K_packed128 #define QUANT_R 1 #define A_TYPE block_q5_K #define A_TYPE_PACKED16 block_q5_K_packed16 +#define A_TYPE_PACKED32 block_q5_K_packed32 +#define DATA_A_QUANT_K #endif #define QUANT_K_Q6_K 256 @@ -356,7 +375,7 @@ struct block_q6_K_packed16 { uint16_t ql[QUANT_K_Q6_K/2/2]; uint16_t qh[QUANT_K_Q6_K/4/2]; - int8_t scales[QUANT_K_Q6_K/16]; + int16_t scales[QUANT_K_Q6_K/16/2]; float16_t d; }; @@ -365,6 +384,7 @@ struct block_q6_K_packed16 #define QUANT_R 1 #define A_TYPE block_q6_K #define A_TYPE_PACKED16 block_q6_K_packed16 +#define DATA_A_QUANT_K #endif // IQuants @@ -1363,18 +1383,11 @@ struct block_mxfp4 uint8_t qs[QUANT_K_MXFP4/2]; }; -//struct block_mxfp4_packed16 -//{ -// uint8_t e; -// uint16_t qs[QUANT_K_MXFP4/2/2]; -//}; - #if defined(DATA_A_MXFP4) #define QUANT_K QUANT_K_MXFP4 #define QUANT_R QUANT_R_MXFP4 #define QUANT_AUXF 1 #define A_TYPE block_mxfp4 -//#define A_TYPE_PACKED16 block_mxfp4_packed16 #endif #if defined(DATA_A_IQ4_NL) || defined(DATA_A_IQ4_XS) @@ -1397,12 +1410,12 @@ void init_iq_shmem(uvec3 wgsize) #endif #if defined(DATA_A_MXFP4) -const FLOAT_TYPE kvalues_mxfp4_const[16] = { - FLOAT_TYPE(0.0f), FLOAT_TYPE(0.5f), FLOAT_TYPE(1.0f), FLOAT_TYPE(1.5f), FLOAT_TYPE(2.0f), FLOAT_TYPE(3.0f), FLOAT_TYPE(4.0f), FLOAT_TYPE(6.0f), - FLOAT_TYPE(-0.0f), FLOAT_TYPE(-0.5f), FLOAT_TYPE(-1.0f), FLOAT_TYPE(-1.5f), FLOAT_TYPE(-2.0f), FLOAT_TYPE(-3.0f), FLOAT_TYPE(-4.0f), FLOAT_TYPE(-6.0f) +const int8_t kvalues_mxfp4_const[16] = { + int8_t(0), int8_t(1), int8_t(2), int8_t(3), int8_t(4), int8_t(6), int8_t(8), int8_t(12), + int8_t(0), int8_t(-1), int8_t(-2), int8_t(-3), int8_t(-4), int8_t(-6), int8_t(-8), int8_t(-12), }; -shared FLOAT_TYPE kvalues_mxfp4[16]; +shared int8_t kvalues_mxfp4[16]; #define NEEDS_INIT_IQ_SHMEM void init_iq_shmem(uvec3 wgsize) diff --git a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/vulkan-shaders-gen.cpp b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/vulkan-shaders-gen.cpp index 0f25ba34..e6ec589f 100644 --- a/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/vulkan-shaders-gen.cpp +++ b/ml/backend/ggml/ggml/src/ggml-vulkan/vulkan-shaders/vulkan-shaders-gen.cpp @@ -566,7 +566,8 @@ void matmul_shaders(bool fp16, MatMulIdType matmul_id_type, bool coopmat, bool c } #if defined(GGML_VULKAN_INTEGER_DOT_GLSLC_SUPPORT) - if (!coopmat && !coopmat2 && matmul_id_type == MatMulIdType::NONE && is_legacy_quant(tname)) { + // Integer dot mmq performs better with f32 accumulators + if (!f16acc && !coopmat && !coopmat2 && (is_legacy_quant(tname) || is_k_quant(tname) || tname == "mxfp4")) { string_to_spv(shader_name + "_" + tname + "_q8_1", "mul_mmq.comp", merge_maps(merge_maps(base_dict, float_type_dict), {{data_a_key, "1"}, {"D_TYPE", "float"},}), fp16, coopmat, coopmat2, f16acc); } #endif @@ -574,7 +575,7 @@ void matmul_shaders(bool fp16, MatMulIdType matmul_id_type, bool coopmat, bool c } void process_shaders() { - std::map base_dict = {{"FLOAT_TYPE", "float"}}; + std::map base_dict = {{"FLOAT_TYPE", "float"}, {"FLOAT_TYPE_VEC2", "vec2"}}; // matmul for (const MatMulIdType& matmul_id_type : {MatMulIdType::NONE, MatMulIdType::DEFAULT, MatMulIdType::SUBGROUP}) { @@ -841,10 +842,14 @@ void process_shaders() { string_to_spv("rope_norm_f32", "rope_norm.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float"}}); string_to_spv("rope_norm_f16", "rope_norm.comp", {{"A_TYPE", "float16_t"}, {"D_TYPE", "float16_t"}}); string_to_spv("rope_norm_f16_rte", "rope_norm.comp", {{"A_TYPE", "float16_t"}, {"D_TYPE", "float16_t"}, {"RTE16", "1"}}); + string_to_spv("rope_norm_f32_f16", "rope_norm.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float16_t"}}); + string_to_spv("rope_norm_f32_f16_rte", "rope_norm.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float16_t"}, {"RTE16", "1"}}); string_to_spv("rope_neox_f32", "rope_neox.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float"}}); string_to_spv("rope_neox_f16", "rope_neox.comp", {{"A_TYPE", "float16_t"}, {"D_TYPE", "float16_t"}}); string_to_spv("rope_neox_f16_rte", "rope_neox.comp", {{"A_TYPE", "float16_t"}, {"D_TYPE", "float16_t"}, {"RTE16", "1"}}); + string_to_spv("rope_neox_f32_f16", "rope_neox.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float16_t"}}); + string_to_spv("rope_neox_f32_f16_rte", "rope_neox.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float16_t"}, {"RTE16", "1"}}); string_to_spv("rope_multi_f32", "rope_multi.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float"}}); string_to_spv("rope_multi_f16", "rope_multi.comp", {{"A_TYPE", "float16_t"}, {"D_TYPE", "float16_t"}}); From 6286d9a3a549c600896a4a7029983a09a8488b56 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Wed, 12 Nov 2025 08:40:38 -0800 Subject: [PATCH 22/32] Enable Vulkan with a temporary opt-in setting (#12931) * docs: vulkan information * Revert "CI: Set up temporary opt-out Vulkan support (#12614)" This reverts commit 8b6e5baee71c6a1588cf062b150f229b2436e1d8. * vulkan: temporary opt-in for Vulkan support Revert this once we're ready to enable by default. * win: add vulkan CI build --- .github/workflows/release.yaml | 38 ++++++++++++++++++------------ .github/workflows/test.yaml | 1 + Dockerfile | 27 +--------------------- discover/runner.go | 3 +++ docs/docker.mdx | 10 ++++++++ docs/gpu.mdx | 42 +++++++++++++++++++++++++++++++++- envconfig/config.go | 6 ++--- scripts/build_linux.sh | 3 +-- 8 files changed, 84 insertions(+), 46 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index ec4d2450..cbf6d293 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -104,6 +104,13 @@ jobs: install: https://download.amd.com/developer/eula/rocm-hub/AMD-Software-PRO-Edition-24.Q4-WinSvr2022-For-HIP.exe rocm-version: '6.2' flags: '-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_FLAGS="-parallel-jobs=4 -Wno-ignored-attributes -Wno-deprecated-pragma" -DCMAKE_CXX_FLAGS="-parallel-jobs=4 -Wno-ignored-attributes -Wno-deprecated-pragma"' + runner_dir: 'rocm' + - os: windows + arch: amd64 + preset: Vulkan + install: https://sdk.lunarg.com/sdk/download/1.4.321.1/windows/vulkansdk-windows-X64-1.4.321.1.exe + flags: '' + runner_dir: 'vulkan' runs-on: ${{ matrix.arch == 'arm64' && format('{0}-{1}', matrix.os, matrix.arch) || matrix.os }} environment: release env: @@ -113,13 +120,14 @@ jobs: run: | choco install -y --no-progress ccache ninja ccache -o cache_dir=${{ github.workspace }}\.ccache - - if: startsWith(matrix.preset, 'CUDA ') || startsWith(matrix.preset, 'ROCm ') + - if: startsWith(matrix.preset, 'CUDA ') || startsWith(matrix.preset, 'ROCm ') || startsWith(matrix.preset, 'Vulkan') id: cache-install uses: actions/cache/restore@v4 with: path: | C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA C:\Program Files\AMD\ROCm + C:\VulkanSDK key: ${{ matrix.install }} - if: startsWith(matrix.preset, 'CUDA ') name: Install CUDA ${{ matrix.cuda-version }} @@ -149,6 +157,18 @@ jobs: echo "HIPCXX=$hipPath\bin\clang++.exe" | Out-File -FilePath $env:GITHUB_ENV -Append echo "HIP_PLATFORM=amd" | Out-File -FilePath $env:GITHUB_ENV -Append echo "CMAKE_PREFIX_PATH=$hipPath" | Out-File -FilePath $env:GITHUB_ENV -Append + - if: matrix.preset == 'Vulkan' + name: Install Vulkan ${{ matrix.rocm-version }} + run: | + $ErrorActionPreference = "Stop" + if ("${{ steps.cache-install.outputs.cache-hit }}" -ne 'true') { + Invoke-WebRequest -Uri "${{ matrix.install }}" -OutFile "install.exe" + Start-Process -FilePath .\install.exe -ArgumentList "-c","--am","--al","in" -NoNewWindow -Wait + } + + $vulkanPath = (Resolve-Path "C:\VulkanSDK\*").path + echo "$vulkanPath\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + echo "VULKAN_SDK=$vulkanPath" >> $env:GITHUB_ENV - if: matrix.preset == 'CPU' run: | echo "CC=clang.exe" | Out-File -FilePath $env:GITHUB_ENV -Append @@ -159,6 +179,7 @@ jobs: path: | C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA C:\Program Files\AMD\ROCm + C:\VulkanSDK key: ${{ matrix.install }} - uses: actions/checkout@v4 - uses: actions/cache@v4 @@ -312,13 +333,13 @@ jobs: include: - os: linux arch: amd64 - target: archive_novulkan + target: archive - os: linux arch: amd64 target: rocm - os: linux arch: arm64 - target: archive_novulkan + target: archive runs-on: ${{ matrix.arch == 'arm64' && format('{0}-{1}', matrix.os, matrix.arch) || matrix.os }} environment: release needs: setup-environment @@ -374,14 +395,12 @@ jobs: include: - os: linux arch: arm64 - target: novulkan build-args: | CGO_CFLAGS CGO_CXXFLAGS GOFLAGS - os: linux arch: amd64 - target: novulkan build-args: | CGO_CFLAGS CGO_CXXFLAGS @@ -394,14 +413,6 @@ jobs: CGO_CXXFLAGS GOFLAGS FLAVOR=rocm - - os: linux - arch: amd64 - suffix: '-vulkan' - target: default - build-args: | - CGO_CFLAGS - CGO_CXXFLAGS - GOFLAGS runs-on: ${{ matrix.arch == 'arm64' && format('{0}-{1}', matrix.os, matrix.arch) || matrix.os }} environment: release needs: setup-environment @@ -419,7 +430,6 @@ jobs: with: context: . platforms: ${{ matrix.os }}/${{ matrix.arch }} - target: ${{ matrix.preset }} build-args: ${{ matrix.build-args }} outputs: type=image,name=${{ vars.DOCKER_REPO }},push-by-digest=true,name-canonical=true,push=true cache-from: type=registry,ref=${{ vars.DOCKER_REPO }}:latest diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index d74da923..82f2b040 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -172,6 +172,7 @@ jobs: path: | C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA C:\Program Files\AMD\ROCm + C:\VulkanSDK key: ${{ matrix.install }} - uses: actions/checkout@v4 - uses: actions/cache@v4 diff --git a/Dockerfile b/Dockerfile index c56c229a..3a936506 100644 --- a/Dockerfile +++ b/Dockerfile @@ -159,32 +159,7 @@ ARG VULKANVERSION COPY --from=cpu dist/lib/ollama /lib/ollama COPY --from=build /bin/ollama /bin/ollama -# Temporary opt-out stages for Vulkan -FROM --platform=linux/amd64 scratch AS amd64_novulkan -# COPY --from=cuda-11 dist/lib/ollama/ /lib/ollama/ -COPY --from=cuda-12 dist/lib/ollama /lib/ollama/ -COPY --from=cuda-13 dist/lib/ollama /lib/ollama/ -FROM arm64 AS arm64_novulkan -FROM ${FLAVOR}_novulkan AS archive_novulkan -COPY --from=cpu dist/lib/ollama /lib/ollama -COPY --from=build /bin/ollama /bin/ollama -FROM ubuntu:24.04 AS novulkan -RUN apt-get update \ - && apt-get install -y ca-certificates \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* -COPY --from=archive_novulkan /bin /usr/bin -ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin -COPY --from=archive_novulkan /lib/ollama /usr/lib/ollama -ENV LD_LIBRARY_PATH=/usr/local/nvidia/lib:/usr/local/nvidia/lib64 -ENV NVIDIA_DRIVER_CAPABILITIES=compute,utility -ENV NVIDIA_VISIBLE_DEVICES=all -ENV OLLAMA_HOST=0.0.0.0:11434 -EXPOSE 11434 -ENTRYPOINT ["/bin/ollama"] -CMD ["serve"] - -FROM ubuntu:24.04 AS default +FROM ubuntu:24.04 RUN apt-get update \ && apt-get install -y ca-certificates libvulkan1 \ && apt-get clean \ diff --git a/discover/runner.go b/discover/runner.go index 4d44dae2..bf2110bc 100644 --- a/discover/runner.go +++ b/discover/runner.go @@ -94,6 +94,9 @@ func GPUDevices(ctx context.Context, runners []ml.FilteredRunnerDiscovery) []ml. continue } else if jetpack != "" && filepath.Base(dir) != "cuda_"+jetpack { continue + } else if !envconfig.EnableVulkan() && strings.Contains(filepath.Base(dir), "vulkan") { + slog.Info("experimental Vulkan support disabled. To enable, set OLLAMA_VULKAN=1") + continue } dirs = []string{ml.LibOllamaPath, dir} } else { diff --git a/docs/docker.mdx b/docs/docker.mdx index 22d2bc33..ba770a70 100644 --- a/docs/docker.mdx +++ b/docs/docker.mdx @@ -68,6 +68,15 @@ To run Ollama using Docker with AMD GPUs, use the `rocm` tag and the following c docker run -d --device /dev/kfd --device /dev/dri -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama:rocm ``` +## Vulkan Support + +Vulkan is bundled into the `ollama/ollama` image. + +```shell +docker run -d --device /dev/kfd --device /dev/dri -v ollama:/root/.ollama -p 11434:11434 -e OLLAMA_VULKAN=1 --name ollama ollama/ollama +``` + + ## Run model locally Now you can run a model: @@ -79,3 +88,4 @@ docker exec -it ollama ollama run llama3.2 ## Try different models More models can be found on the [Ollama library](https://ollama.com/library). + diff --git a/docs/gpu.mdx b/docs/gpu.mdx index bf114221..36bfd3da 100644 --- a/docs/gpu.mdx +++ b/docs/gpu.mdx @@ -52,7 +52,11 @@ sudo modprobe nvidia_uvm` ## AMD Radeon -Ollama supports the following AMD GPUs: +Ollama supports the following AMD GPUs via the ROCm library: + +> [!NOTE] +> Additional AMD GPU support is provided by the Vulkan Library - see below. + ### Linux Support @@ -124,3 +128,39 @@ accessing the AMD GPU devices. On the host system you can run ## Metal (Apple GPUs) Ollama supports GPU acceleration on Apple devices via the Metal API. + + +## Vulkan GPU Support + +> [!NOTE] +> Vulkan is currently an Experimental feature. To enable, you must set OLLAMA_VULKAN=1 for the Ollama server as +described in the [FAQ](faq.md#how-do-i-configure-ollama-server) + +Additional GPU support on Windows and Linux is provided via +[Vulkan](https://www.vulkan.org/). On Windows most GPU vendors drivers come +bundled with Vulkan support and require no additional setup steps. Most Linux +distributions require installing additional components, and you may have +multiple options for Vulkan drivers between Mesa and GPU Vendor specific packages + +- Linux Intel GPU Instructions - https://dgpu-docs.intel.com/driver/client/overview.html +- Linux AMD GPU Instructions - https://amdgpu-install.readthedocs.io/en/latest/install-script.html#specifying-a-vulkan-implementation + +For AMD GPUs on some Linux distributions, you may need to add the `ollama` user to the `render` group. + +The Ollama scheduler leverages available VRAM data reported by the GPU libraries to +make optimal scheduling decisions. Vulkan requires additional capabilities or +running as root to expose this available VRAM data. If neither root access or this +capability are granted, Ollama will use approximate sizes of the models +to make best effort scheduling decisions. + +```bash +sudo setcap cap_perfmon+ep /usr/local/bin/ollama +``` + +### GPU Selection + +To select specific Vulkan GPU(s), you can set the environment variable +`GGML_VK_VISIBLE_DEVICES` to one or more numeric IDs on the Ollama server as +described in the [FAQ](faq.md#how-do-i-configure-ollama-server). If you +encounter any problems with Vulkan based GPUs, you can disable all Vulkan GPUs +by setting `GGML_VK_VISIBLE_DEVICES=-1` \ No newline at end of file diff --git a/envconfig/config.go b/envconfig/config.go index d155bd8f..238e5e6e 100644 --- a/envconfig/config.go +++ b/envconfig/config.go @@ -196,8 +196,6 @@ var ( NoPrune = Bool("OLLAMA_NOPRUNE") // SchedSpread allows scheduling models across all GPUs. SchedSpread = Bool("OLLAMA_SCHED_SPREAD") - // IntelGPU enables experimental Intel GPU detection. - IntelGPU = Bool("OLLAMA_INTEL_GPU") // MultiUserCache optimizes prompt caching for multi-user scenarios MultiUserCache = Bool("OLLAMA_MULTIUSER_CACHE") // Enable the new Ollama engine @@ -206,6 +204,8 @@ var ( ContextLength = Uint("OLLAMA_CONTEXT_LENGTH", 4096) // Auth enables authentication between the Ollama client and server UseAuth = Bool("OLLAMA_AUTH") + // Enable Vulkan backend + EnableVulkan = Bool("OLLAMA_VULKAN") ) func String(s string) func() string { @@ -314,7 +314,7 @@ func AsMap() map[string]EnvVar { ret["GGML_VK_VISIBLE_DEVICES"] = EnvVar{"GGML_VK_VISIBLE_DEVICES", VkVisibleDevices(), "Set which Vulkan devices are visible by numeric ID"} ret["GPU_DEVICE_ORDINAL"] = EnvVar{"GPU_DEVICE_ORDINAL", GpuDeviceOrdinal(), "Set which AMD devices are visible by numeric ID"} ret["HSA_OVERRIDE_GFX_VERSION"] = EnvVar{"HSA_OVERRIDE_GFX_VERSION", HsaOverrideGfxVersion(), "Override the gfx used for all detected AMD GPUs"} - ret["OLLAMA_INTEL_GPU"] = EnvVar{"OLLAMA_INTEL_GPU", IntelGPU(), "Enable experimental Intel GPU detection"} + ret["OLLAMA_VULKAN"] = EnvVar{"OLLAMA_VULKAN", EnableVulkan(), "Enable experimental Vulkan support"} } return ret diff --git a/scripts/build_linux.sh b/scripts/build_linux.sh index 8287c11c..618722d1 100755 --- a/scripts/build_linux.sh +++ b/scripts/build_linux.sh @@ -13,13 +13,12 @@ set -eu . $(dirname $0)/env.sh mkdir -p dist -NOVULKAN=${NOVULKAN:-""} docker buildx build \ --output type=local,dest=./dist/ \ --platform=${PLATFORM} \ ${OLLAMA_COMMON_BUILD_ARGS} \ - --target archive${NOVULKAN} \ + --target archive \ -f Dockerfile \ . From 8224cd9063f29fe1bd775d386988aac8e6fd2ea6 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Wed, 12 Nov 2025 10:32:24 -0800 Subject: [PATCH 23/32] ci: fix win vulkan (#13062) --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index cbf6d293..a7d0cf3b 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -192,7 +192,7 @@ jobs: Enter-VsDevShell -VsInstallPath 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise' -SkipAutomaticLocation -DevCmdArguments '-arch=x64 -no_logo' cmake --preset "${{ matrix.preset }}" ${{ matrix.flags }} --install-prefix "$((pwd).Path)\dist\${{ matrix.os }}-${{ matrix.arch }}" cmake --build --parallel ([Environment]::ProcessorCount) --preset "${{ matrix.preset }}" - cmake --install build --component "${{ startsWith(matrix.preset, 'CUDA ') && 'CUDA' || startsWith(matrix.preset, 'ROCm ') && 'HIP' || 'CPU' }}" --strip + cmake --install build --component "${{ startsWith(matrix.preset, 'CUDA ') && 'CUDA' || startsWith(matrix.preset, 'ROCm ') && 'HIP' || startsWith(matrix.preset, 'Vulkan') && 'Vulkan' || 'CPU' }}" --strip Remove-Item -Path dist\lib\ollama\rocm\rocblas\library\*gfx906* -ErrorAction SilentlyContinue env: CMAKE_GENERATOR: Ninja From f206357412cfac5a885b843bd9a1c64ec89ac213 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Wed, 12 Nov 2025 17:00:16 -0800 Subject: [PATCH 24/32] readme: fix incorrect header in community integrations (#13065) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2139f7bd..6fd5881f 100644 --- a/README.md +++ b/README.md @@ -640,5 +640,5 @@ See the [API documentation](./docs/api.md) for all endpoints. - [Langfuse](https://langfuse.com/docs/integrations/ollama) is an open source LLM observability platform that enables teams to collaboratively monitor, evaluate and debug AI applications. - [MLflow Tracing](https://mlflow.org/docs/latest/llms/tracing/index.html#automatic-tracing) is an open source LLM observability tool with a convenient API to log and visualize traces, making it easy to debug and evaluate GenAI applications. -## Security +### Security - [Ollama Fortress](https://github.com/ParisNeo/ollama_proxy_server) From 8a75d8b0154511d2bafe16f230e9268ee7a511da Mon Sep 17 00:00:00 2001 From: Radhi <153525225+Bajahaw@users.noreply.github.com> Date: Thu, 13 Nov 2025 02:08:50 +0100 Subject: [PATCH 25/32] readme: add AI UI to community integrations (#13035) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6fd5881f..cf1d04d1 100644 --- a/README.md +++ b/README.md @@ -299,6 +299,7 @@ See the [API documentation](./docs/api.md) for all endpoints. - [LibreChat](https://github.com/danny-avila/LibreChat) - [Bionic GPT](https://github.com/bionic-gpt/bionic-gpt) - [HTML UI](https://github.com/rtcfirefly/ollama-ui) +- [AI-UI](https://github.com/bajahaw/ai-ui) - [Saddle](https://github.com/jikkuatwork/saddle) - [TagSpaces](https://www.tagspaces.org) (A platform for file-based apps, [utilizing Ollama](https://docs.tagspaces.org/ai/) for the generation of tags and descriptions) - [Chatbot UI](https://github.com/ivanfioravanti/chatbot-ollama) From 54a76d377301f9b1bcf00895a857e84cc45f0b3a Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Wed, 12 Nov 2025 20:37:43 -0800 Subject: [PATCH 26/32] app: remove source code for previous JavaScript-based macOS app (#13067) The code in this directory has been replaced with the new Go version in the 'app' directory. --- macapp/.eslintrc.json | 16 - macapp/.gitignore | 92 - macapp/README.md | 21 - macapp/assets/icon.icns | Bin 239658 -> 0 bytes macapp/assets/iconDarkTemplate.png | Bin 402 -> 0 bytes macapp/assets/iconDarkTemplate@2x.png | Bin 741 -> 0 bytes macapp/assets/iconDarkUpdateTemplate.png | Bin 440 -> 0 bytes macapp/assets/iconDarkUpdateTemplate@2x.png | Bin 763 -> 0 bytes macapp/assets/iconTemplate.png | Bin 447 -> 0 bytes macapp/assets/iconTemplate@2x.png | Bin 891 -> 0 bytes macapp/assets/iconUpdateTemplate.png | Bin 443 -> 0 bytes macapp/assets/iconUpdateTemplate@2x.png | Bin 844 -> 0 bytes macapp/forge.config.ts | 79 - macapp/package-lock.json | 16604 ------------------ macapp/package.json | 80 - macapp/postcss.config.js | 7 - macapp/src/app.css | 34 - macapp/src/app.tsx | 122 - macapp/src/declarations.d.ts | 4 - macapp/src/index.html | 9 - macapp/src/index.ts | 302 - macapp/src/install.ts | 21 - macapp/src/ollama.svg | 9 - macapp/src/preload.ts | 0 macapp/src/renderer.tsx | 7 - macapp/tailwind.config.js | 6 - macapp/tsconfig.json | 20 - macapp/webpack.main.config.ts | 20 - macapp/webpack.plugins.ts | 14 - macapp/webpack.renderer.config.ts | 19 - macapp/webpack.rules.ts | 35 - 31 files changed, 17521 deletions(-) delete mode 100644 macapp/.eslintrc.json delete mode 100644 macapp/.gitignore delete mode 100644 macapp/README.md delete mode 100644 macapp/assets/icon.icns delete mode 100644 macapp/assets/iconDarkTemplate.png delete mode 100644 macapp/assets/iconDarkTemplate@2x.png delete mode 100644 macapp/assets/iconDarkUpdateTemplate.png delete mode 100644 macapp/assets/iconDarkUpdateTemplate@2x.png delete mode 100644 macapp/assets/iconTemplate.png delete mode 100644 macapp/assets/iconTemplate@2x.png delete mode 100644 macapp/assets/iconUpdateTemplate.png delete mode 100644 macapp/assets/iconUpdateTemplate@2x.png delete mode 100644 macapp/forge.config.ts delete mode 100644 macapp/package-lock.json delete mode 100644 macapp/package.json delete mode 100644 macapp/postcss.config.js delete mode 100644 macapp/src/app.css delete mode 100644 macapp/src/app.tsx delete mode 100644 macapp/src/declarations.d.ts delete mode 100644 macapp/src/index.html delete mode 100644 macapp/src/index.ts delete mode 100644 macapp/src/install.ts delete mode 100644 macapp/src/ollama.svg delete mode 100644 macapp/src/preload.ts delete mode 100644 macapp/src/renderer.tsx delete mode 100644 macapp/tailwind.config.js delete mode 100644 macapp/tsconfig.json delete mode 100644 macapp/webpack.main.config.ts delete mode 100644 macapp/webpack.plugins.ts delete mode 100644 macapp/webpack.renderer.config.ts delete mode 100644 macapp/webpack.rules.ts diff --git a/macapp/.eslintrc.json b/macapp/.eslintrc.json deleted file mode 100644 index 2d7aa607..00000000 --- a/macapp/.eslintrc.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "env": { - "browser": true, - "es6": true, - "node": true - }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended", - "plugin:import/recommended", - "plugin:import/electron", - "plugin:import/typescript" - ], - "parser": "@typescript-eslint/parser" -} diff --git a/macapp/.gitignore b/macapp/.gitignore deleted file mode 100644 index 8296128d..00000000 --- a/macapp/.gitignore +++ /dev/null @@ -1,92 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock -.DS_Store - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.test - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# next.js build output -.next - -# nuxt.js build output -.nuxt - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# Webpack -.webpack/ - -# Vite -.vite/ - -# Electron-Forge -out/ diff --git a/macapp/README.md b/macapp/README.md deleted file mode 100644 index bdaf05e7..00000000 --- a/macapp/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Desktop - -This app builds upon Ollama to provide a desktop experience for running models. - -## Developing - -First, build the `ollama` binary: - -```shell -cd .. -go build . -``` - -Then run the desktop app with `npm start`: - -```shell -cd macapp -npm install -npm start -``` - diff --git a/macapp/assets/icon.icns b/macapp/assets/icon.icns deleted file mode 100644 index 308592aac6550a7fbde0a7d8f13c53e37778adf9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 239658 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

t7Lau@(YH|$C5a}QY*~f%OayMOL6ly;N<5=G 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�sv5>E;B!+`$eJDeX>_9HQfprM*BPzx{8jpaZL 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%0AGw%}-Y0sam;;Vs~2W^{wA5jh5 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 Zep=tbndE69-~jV<4(~v0L|@PI{{z>tL)-uW diff --git a/macapp/assets/iconDarkTemplate.png b/macapp/assets/iconDarkTemplate.png deleted file mode 100644 index 853d350804d130f44ad618b3e64fe538537e62a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 402 zcmV;D0d4+?P)eU*)8s4? z_(TdjI{$|uyx5CU68+>4jNu4pI-bWB>Y*=;EmFlchQ;hZhzV?=h6m{qtEiySLn~*+ zRpj2IK;Xu;odvyKis{W4u65Ba#*q`#{8?9#U(DbK&tm#>s5uv6PGwqi|Lr0P>;6rz z=bF4J=BP8+z%ouWr{jq2c#`&>(^$d*_A#$-59S|TMc$>ed98or7I%7Y&%L^eoQnVH w9NP=+o}|S;zFkGu#6Q+uMFzz`2BhE71Q#<}?s@#GQ2+n{07*qoM6N<$f){tF*Z=?k diff --git a/macapp/assets/iconDarkTemplate@2x.png b/macapp/assets/iconDarkTemplate@2x.png deleted file mode 100644 index 1a94c9e27d9b7ba048ff37175c37d5f3be18e322..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 741 zcmV z732f?fhLqEZ^(b;8Oa6g=R5YVbV8|hP91%>oE{C>A5@!P$bB0{h@^HZZ zsoD&Zel0&nO65^r^?I^f%M)Y?;YX4Uh~ z0(y}`1k)1ZX4ZbK?>1n$|dQiSh!{jP?0h4m&p*mvy zJ_3^)wmW`$5TRSB+_ph&?;v9^`M#taDj%og)L13@wFvp z0&*|L;E!cdpVmL>fCU1O59Dp@Dz{3Asaoz6TIaL}%R&z9X1}+LG-) zPj-0Bh2W}ucjl>wq4R+(VRZnu2d^;eY)I(^! zuEZKHf(fry?|>w?PIoZh4s1gBTQRL932i{c1lA%utwF>Dn)+X^j^-j_0p=3q*1FTqENU>1XY_OLV3kw$ZHY{u;vL0k1KW0s-q5Lk! z_jb?A@#eiZ=G3QobMC$8-Cr*)<&i@cPZ9aXhz)sw9y7hNm{N7w(TL(>}84uz&zay^5HQI57WAXd5sD0MNpNcEAg-ctc#yl@$ zL7(T*rR#Us;0qhL#iJy|f&8wyfL_dDR{UuN;r1hs;UJMRjal>Dh9eAN1a+pZPvxSD z9LnEt7j9IG-$_E)A_Ykf_x6&RfxE%0;ZA!JeJ6@KD;E0000#j#KxMRNzlHScc!JC$3rVDimRkN5Qnl>hPg1dhbdg@meneW9EyZ`! zvdf7{^de9dD~Kqv?CFr1mn?g0){D9_?oC+Zy)d7|R|Dj$Ww#Khb?~2g7JtcF1Smr1 zdM}E^T8W&xH-ibp+(8aiXCVQ5=}ysn$F<#6+sNMGkC)n_|Q9Z0fL6Lpw6aY;VKh(#YfG?bBq`~!!OPxVBMk)(sGPb@~d z>>;Bt2U&@+8m4!=K!99H_-wmXCDu5(h(Nje#BJnP0|n$t;sJ6r71etbkShr}u8*Ra zYDnKX?R&Mf0IT5c6ImqdA;R>BqA_GNwEmn42z5ss=?Be_1Vm>7k`TH*6KTSuVGRZ* taUmsMEpGy%fxskOMQA4?2{=z}{{aP#Wiwn~FQotg002ovPDHLkV1jHGTDSlJ diff --git a/macapp/assets/iconTemplate.png b/macapp/assets/iconTemplate.png deleted file mode 100644 index 7b0882546cdb521c7636bcf4dc6dcebf50e6e65f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 447 zcmV;w0YLtVP)MiLSOp88`^^WyXCvXLG{7^Reaij zM%>#w%lNi@9yfkaT~MTi2)&p_tL1g@o)zf9k{^@{if9*;=FFBcj7uzH1{Fz(ek*c- zmEf6nv|yd;zKkUmImfPK#}3Lq;rHZ8;MtLE=*> zSZ>WG(miU%Ee7%IJMl-X_hJsS7_#MQfhL<>_hKCa pLr<)n)QNsz70He-N!gQs#}6K(KN!n|$guzb002ovPDHLkV1hFm$wB}C diff --git a/macapp/assets/iconTemplate@2x.png b/macapp/assets/iconTemplate@2x.png deleted file mode 100644 index 0be1e88ec51893297fc4bc3f8388717644f24472..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 891 zcmV->1BCpEP)K~#7F#aB&8 zRY4T~v?#GKE2BTkr;9|f(C7~edWr5Lg4!C=CTkT$)FRNRRZ1ANil9Y{pxQ)5L4iR( z2vHJ+7*tAx_9K>wSx4tXaU{SB;ehZD*(aQv#ePx9{uKkt8)xZg(s6n;i~V8R{1X1A-i`zazD5H% zNBv$F`xmsiAUsX|Ir2m~>{o9P?xy}-SSTzKrpN+eiDRD@HahLtWtfS0R5I%xTX@46+3<>FCYEwN6|2 zMu+g0aGtPU*aov4iv7Y*j{P^}fg0E~bom6)Yq|_(siMZLLXJowGoL@R-3|MdapRuS z)=gb)FGUV`=)Cv<%*M+Vnkffi_Ky0x>pQRkHu614j(x~X&na<+#yyxmyr$0+;f}%x z<295IoM+pRx%O#c7fcp(Q+MmeW?_@lzEA1;h{YX7dy4HAwzZJx?(c-1(g`wXw+olR zo&(MFah~lf;g?0tzyM`XY&KGAE*}#ff|;Ey!a3wRVLwbQXoWqpYbdKJ-@*eskeRQdS@yk4RsXQ+lQ0cGxMcL~>L*{u+D+6vO1Q4$$IKg#4kt zf@3{04WlZdR=k_ktC0OaU@EMHtr1s!2Wi2XmODKWWQ`U#ub-6Q`mGns1xSuk*gen` z?u=T3I!fK;qeu>|eErnx;SoR?C84+FD(XYw&c9N4;SkW` R<$M4D002ovPDHLkV1j3sofiNA diff --git a/macapp/assets/iconUpdateTemplate.png b/macapp/assets/iconUpdateTemplate.png deleted file mode 100644 index fa9d715812e161a65971d6ab209bd49f91e88bdd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 443 zcmV;s0Yv_ZP)UqK~#7Ft<%3N zMPV4g@rQc{gLI|DqBF>3xI|KlMFs;4S>zU*k&?}S;Q9wHHX8?deR3|4)iB lP26(b{Rq%6e((M#z5xc(NGDo69w7h#002ovPDHLkV1k=-`;1Pwbx#I1OEX=B+v{K9ujU9 z_6qwFjM*>To@SzG(Di+HTZ{tDNY*(Btzr z;e-nM%hWs*ZZr-}jQIwJBOXg=Zwhbmd6aNIp9|zzwinT=_L1;A+l{CJ!Jlkk`z^u{ zmrZ~4T?_RkwhzR8q*^2b{G|O#Tg2Z>V&4tWexR+ie19+!@fe_H6a27G<9kg~-38Z# zgTjTvGqj4$bbN-4qHXuYUM zN*p&6w4%zI#-Isda=uD4&tzdg2cAalm}TZWnsm?(gi)A@d~g9V)zh^*f&%preh3xo zg=2(w*q$mpNUc`G9{4$Qo&A%9>x3Qr_!GXLi-b7FfltlAATnC{^h6E9`$hQK^moyz z=WL(n94&0WbA8ZNtX|U=gmuCdrf;e6BK&M`5RMm)gTL;2ZfJdIhrcrv%W2opmXHvd z!GrYt2;XVw_XuzEeL*-{_yB&yJnHVUf3@(GaDn+DPuyg*nTEj0P`{3x)!7entCx)djn0~RApw6&4GqH%5u{9|3Of4Y@hD>zV3=K?0G zoVyU-j9jqQGhjWM+L4rQAb7-1qcAw4{}lQ&8)Zj#3osZDwhwJ0FG zM^s|m4Gq(cumRbj^`M-^0Y#$`XNf{JN6JW0uy#}2k9^>zbwIJq3|fhN;L!nRlK%$h W0P|S^muvd~0000 path.join(__dirname, '../dist/darwin-amd64/lib/ollama', f)), - path.join(__dirname, './assets/iconTemplate.png'), - path.join(__dirname, './assets/iconTemplate@2x.png'), - path.join(__dirname, './assets/iconUpdateTemplate.png'), - path.join(__dirname, './assets/iconUpdateTemplate@2x.png'), - path.join(__dirname, './assets/iconDarkTemplate.png'), - path.join(__dirname, './assets/iconDarkTemplate@2x.png'), - path.join(__dirname, './assets/iconDarkUpdateTemplate.png'), - path.join(__dirname, './assets/iconDarkUpdateTemplate@2x.png'), - ], - ...(process.env.SIGN - ? { - osxSign: { - identity: process.env.APPLE_IDENTITY, - }, - osxNotarize: { - tool: 'notarytool', - appleId: process.env.APPLE_ID || '', - appleIdPassword: process.env.APPLE_PASSWORD || '', - teamId: process.env.APPLE_TEAM_ID || '', - }, - } - : {}), - osxUniversal: { - x64ArchFiles: '*', - }, - }, - rebuildConfig: {}, - makers: [new MakerSquirrel({}), new MakerZIP({}, ['darwin'])], - hooks: { - readPackageJson: async (_, packageJson) => { - return { ...packageJson, version: process.env.VERSION || packageJson.version } - }, - }, - plugins: [ - new AutoUnpackNativesPlugin({}), - new WebpackPlugin({ - mainConfig, - devContentSecurityPolicy: `default-src * 'unsafe-eval' 'unsafe-inline'; img-src data: 'self'`, - renderer: { - config: rendererConfig, - nodeIntegration: true, - entryPoints: [ - { - html: './src/index.html', - js: './src/renderer.tsx', - name: 'main_window', - preload: { - js: './src/preload.ts', - }, - }, - ], - }, - }), - ], -} - -export default config diff --git a/macapp/package-lock.json b/macapp/package-lock.json deleted file mode 100644 index bacc2a37..00000000 --- a/macapp/package-lock.json +++ /dev/null @@ -1,16604 +0,0 @@ -{ - "name": "ollama", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "ollama", - "version": "0.0.0", - "license": "MIT", - "dependencies": { - "@electron/remote": "^2.0.10", - "@heroicons/react": "^2.0.18", - "@segment/analytics-node": "^1.0.0", - "copy-to-clipboard": "^3.3.3", - "electron-squirrel-startup": "^1.0.0", - "electron-store": "^8.1.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "uuid": "^9.0.0", - "winston": "^3.10.0", - "winston-daily-rotate-file": "^4.7.1" - }, - "devDependencies": { - "@babel/core": "^7.22.5", - "@babel/preset-react": "^7.22.5", - "@electron-forge/cli": "^6.2.1", - "@electron-forge/maker-deb": "^6.2.1", - "@electron-forge/maker-rpm": "^6.2.1", - "@electron-forge/maker-squirrel": "^6.2.1", - "@electron-forge/maker-zip": "^6.2.1", - "@electron-forge/plugin-auto-unpack-natives": "^6.2.1", - "@electron-forge/plugin-webpack": "^6.2.1", - "@electron-forge/publisher-github": "^6.2.1", - "@electron/universal": "^1.4.1", - "@svgr/webpack": "^8.0.1", - "@types/chmodr": "^1.0.0", - "@types/node": "^20.4.0", - "@types/react": "^18.2.14", - "@types/react-dom": "^18.2.6", - "@types/uuid": "^9.0.2", - "@typescript-eslint/eslint-plugin": "^5.60.0", - "@typescript-eslint/parser": "^5.60.0", - "@vercel/webpack-asset-relocator-loader": "^1.7.3", - "babel-loader": "^9.1.2", - "chmodr": "^1.2.0", - "copy-webpack-plugin": "^11.0.0", - "css-loader": "^6.8.1", - "electron": "25.9.2", - "eslint": "^8.43.0", - "eslint-plugin-import": "^2.27.5", - "fork-ts-checker-webpack-plugin": "^7.3.0", - "node-loader": "^2.0.0", - "postcss": "^8.4.24", - "postcss-import": "^15.1.0", - "postcss-loader": "^7.3.3", - "postcss-preset-env": "^8.5.1", - "style-loader": "^3.3.3", - "svg-inline-loader": "^0.8.2", - "tailwindcss": "^3.3.2", - "ts-loader": "^9.4.3", - "ts-node": "^10.9.1", - "typescript": "~4.5.4", - "url-loader": "^4.1.1", - "webpack": "^5.88.0", - "webpack-cli": "^5.1.4", - "webpack-dev-server": "^4.15.1" - } - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", - "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz", - "integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helpers": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz", - "integrity": "sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz", - "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.5", - "browserslist": "^4.21.9", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.9.tgz", - "integrity": "sha512-Pwyi89uO4YrGKxL/eNJ8lfEH55DnRloGPOseaA8NFNL6jAUnn+KccaISiFazCj5IolPPDjGSdzQzXVzODVRqUQ==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz", - "integrity": "sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "regexpu-core": "^5.3.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.1.tgz", - "integrity": "sha512-kX4oXixDxG197yhX+J3Wp+NpL2wuCFjWQAr6yX2jtCnflK9ulMI51ULFGIrWiX1jGfvAxdHp+XQCcP2bZGPs9A==", - "dev": true, - "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0-0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz", - "integrity": "sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz", - "integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz", - "integrity": "sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-wrap-function": "^7.22.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz", - "integrity": "sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.9.tgz", - "integrity": "sha512-sZ+QzfauuUEfxSEjKFmi3qDSHgLsTPK/pEpoD/qonZKOtTPTLbf59oabPQ4rKekt9lFcj/hTZaOhWwFYrgjk+Q==", - "dev": true, - "dependencies": { - "@babel/helper-function-name": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.5.tgz", - "integrity": "sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz", - "integrity": "sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz", - "integrity": "sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", - "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", - "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", - "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.22.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.7.tgz", - "integrity": "sha512-7HmE7pk/Fmke45TODvxvkxRMV9RazV+ZZzhOL9AG8G29TLrr3jkjwF7uJfxZ30EoXpO+LJkq4oA8NjO2DTnEDg==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.5", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", - "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", - "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz", - "integrity": "sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", - "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", - "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz", - "integrity": "sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA==", - "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz", - "integrity": "sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-classes/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", - "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/template": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz", - "integrity": "sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", - "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", - "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz", - "integrity": "sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", - "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", - "dev": true, - "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz", - "integrity": "sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz", - "integrity": "sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", - "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", - "dev": true, - "dependencies": { - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz", - "integrity": "sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", - "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz", - "integrity": "sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", - "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz", - "integrity": "sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ==", - "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz", - "integrity": "sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==", - "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz", - "integrity": "sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==", - "dev": true, - "dependencies": { - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", - "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", - "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", - "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", - "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz", - "integrity": "sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz", - "integrity": "sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz", - "integrity": "sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", - "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz", - "integrity": "sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.6.tgz", - "integrity": "sha512-Vd5HiWml0mDVtcLHIoEU5sw6HOUW/Zk0acLs/SAeuLzkGNOPc9DB4nkUajemhCmTIz3eiaKREZn2hQQqF79YTg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz", - "integrity": "sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", - "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", - "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz", - "integrity": "sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", - "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.22.5.tgz", - "integrity": "sha512-BF5SXoO+nX3h5OhlN78XbbDrBOffv+AxPP2ENaJOVqjWCgBDeOY3WcaUcddutGSfoap+5NEQ/q/4I3WZIvgkXA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.22.5.tgz", - "integrity": "sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.5.tgz", - "integrity": "sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.22.5", - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", - "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", - "dev": true, - "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.22.5.tgz", - "integrity": "sha512-gP4k85wx09q+brArVinTXhWiyzLl9UpmGva0+mWyKxk6JZequ05x3eUcIUE+FyttPKJFRRVtAvQaJ6YF9h1ZpA==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz", - "integrity": "sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "regenerator-transform": "^0.15.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", - "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", - "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", - "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", - "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", - "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", - "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.9.tgz", - "integrity": "sha512-BnVR1CpKiuD0iobHPaM1iLvcwPYN2uVFAqoLVSpEDKWuOikoCv5HbKLxclhKYUXlWkX86DoZGtqI4XhbOsyrMg==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.9", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-typescript": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz", - "integrity": "sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz", - "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", - "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz", - "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.9.tgz", - "integrity": "sha512-wNi5H/Emkhll/bqPjsjQorSykrlfY5OWakd6AulLvMEytpKasMVUpVy8RL4qBIBs5Ac6/5i0/Rv0b/Fg6Eag/g==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.5", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.5", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.22.5", - "@babel/plugin-syntax-import-attributes": "^7.22.5", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.22.5", - "@babel/plugin-transform-async-generator-functions": "^7.22.7", - "@babel/plugin-transform-async-to-generator": "^7.22.5", - "@babel/plugin-transform-block-scoped-functions": "^7.22.5", - "@babel/plugin-transform-block-scoping": "^7.22.5", - "@babel/plugin-transform-class-properties": "^7.22.5", - "@babel/plugin-transform-class-static-block": "^7.22.5", - "@babel/plugin-transform-classes": "^7.22.6", - "@babel/plugin-transform-computed-properties": "^7.22.5", - "@babel/plugin-transform-destructuring": "^7.22.5", - "@babel/plugin-transform-dotall-regex": "^7.22.5", - "@babel/plugin-transform-duplicate-keys": "^7.22.5", - "@babel/plugin-transform-dynamic-import": "^7.22.5", - "@babel/plugin-transform-exponentiation-operator": "^7.22.5", - "@babel/plugin-transform-export-namespace-from": "^7.22.5", - "@babel/plugin-transform-for-of": "^7.22.5", - "@babel/plugin-transform-function-name": "^7.22.5", - "@babel/plugin-transform-json-strings": "^7.22.5", - "@babel/plugin-transform-literals": "^7.22.5", - "@babel/plugin-transform-logical-assignment-operators": "^7.22.5", - "@babel/plugin-transform-member-expression-literals": "^7.22.5", - "@babel/plugin-transform-modules-amd": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.22.5", - "@babel/plugin-transform-modules-systemjs": "^7.22.5", - "@babel/plugin-transform-modules-umd": "^7.22.5", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.22.5", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.5", - "@babel/plugin-transform-numeric-separator": "^7.22.5", - "@babel/plugin-transform-object-rest-spread": "^7.22.5", - "@babel/plugin-transform-object-super": "^7.22.5", - "@babel/plugin-transform-optional-catch-binding": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.6", - "@babel/plugin-transform-parameters": "^7.22.5", - "@babel/plugin-transform-private-methods": "^7.22.5", - "@babel/plugin-transform-private-property-in-object": "^7.22.5", - "@babel/plugin-transform-property-literals": "^7.22.5", - "@babel/plugin-transform-regenerator": "^7.22.5", - "@babel/plugin-transform-reserved-words": "^7.22.5", - "@babel/plugin-transform-shorthand-properties": "^7.22.5", - "@babel/plugin-transform-spread": "^7.22.5", - "@babel/plugin-transform-sticky-regex": "^7.22.5", - "@babel/plugin-transform-template-literals": "^7.22.5", - "@babel/plugin-transform-typeof-symbol": "^7.22.5", - "@babel/plugin-transform-unicode-escapes": "^7.22.5", - "@babel/plugin-transform-unicode-property-regex": "^7.22.5", - "@babel/plugin-transform-unicode-regex": "^7.22.5", - "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.4", - "babel-plugin-polyfill-corejs3": "^0.8.2", - "babel-plugin-polyfill-regenerator": "^0.5.1", - "core-js-compat": "^3.31.0", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", - "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-react": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.22.5.tgz", - "integrity": "sha512-M+Is3WikOpEJHgR385HbuCITPTaPRaNkibTEa9oiofmJvIsrceb4yp9RL9Kb+TE8LznmeyZqpP+Lopwcx59xPQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.5", - "@babel/plugin-transform-react-display-name": "^7.22.5", - "@babel/plugin-transform-react-jsx": "^7.22.5", - "@babel/plugin-transform-react-jsx-development": "^7.22.5", - "@babel/plugin-transform-react-pure-annotations": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.22.5.tgz", - "integrity": "sha512-YbPaal9LxztSGhmndR46FmAbkJ/1fAsw293tSU+I5E5h+cnJ3d4GTwyUgGYmOXJYdGA+uNePle4qbaRzj2NISQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.22.5", - "@babel/plugin-transform-typescript": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", - "dev": true - }, - "node_modules/@babel/runtime": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz", - "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==", - "dev": true, - "dependencies": { - "regenerator-runtime": "^0.13.11" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@csstools/cascade-layer-name-parser": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-1.0.2.tgz", - "integrity": "sha512-xm7Mgwej/wBfLoK0K5LfntmPJzoULayl1XZY9JYgQgT29JiqNw++sLnx95u5y9zCihblzkyaRYJrsRMhIBzRdg==", - "dev": true, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^2.1.1", - "@csstools/css-tokenizer": "^2.1.1" - } - }, - "node_modules/@csstools/color-helpers": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-2.1.0.tgz", - "integrity": "sha512-OWkqBa7PDzZuJ3Ha7T5bxdSVfSCfTq6K1mbAhbO1MD+GSULGjrp45i5RudyJOedstSarN/3mdwu9upJE7gDXfw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": "^14 || ^16 || >=18" - } - }, - "node_modules/@csstools/css-calc": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-1.1.1.tgz", - "integrity": "sha512-Nh+iLCtjlooTzuR0lpmB8I6hPX/VupcGQ3Z1U2+wgJJ4fa8+cWkub+lCsbZcYPzBGsZLEL8fQAg+Na5dwEFJxg==", - "dev": true, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^2.1.1", - "@csstools/css-tokenizer": "^2.1.1" - } - }, - "node_modules/@csstools/css-color-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-1.2.1.tgz", - "integrity": "sha512-NcmaoJIEycIH0HnzZRrwRcBljPh1AWcXl4CNL8MAD3+Zy8XyIpdTtTMaY/phnLHHIYkyjaoSTdxAecss6+PCcg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/color-helpers": "^2.1.0", - "@csstools/css-calc": "^1.1.1" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^2.1.1", - "@csstools/css-tokenizer": "^2.1.1" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.2.0.tgz", - "integrity": "sha512-9BoQ/jSrPq4vv3b9jjLW+PNNv56KlDH5JMx5yASSNrCtvq70FCNZUjXRvbCeR9hYj9ZyhURtqpU/RFIgg6kiOw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^2.1.1" - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.1.1.tgz", - "integrity": "sha512-GbrTj2Z8MCTUv+52GE0RbFGM527xuXZ0Xa5g0Z+YN573uveS4G0qi6WNOMyz3yrFM/jaILTTwJ0+umx81EzqfA==", - "dev": true, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - }, - "node_modules/@csstools/media-query-list-parser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.1.tgz", - "integrity": "sha512-pUjtFbaKbiFNjJo8pprrIaXLvQvWIlwPiFnRI4sEnc4F0NIGTOsw8kaJSR3CmZAKEvV8QYckovgAnWQC0bgLLQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^2.2.0", - "@csstools/css-tokenizer": "^2.1.1" - } - }, - "node_modules/@csstools/postcss-cascade-layers": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-3.0.1.tgz", - "integrity": "sha512-dD8W98dOYNOH/yX4V4HXOhfCOnvVAg8TtsL+qCGNoKXuq5z2C/d026wGWgySgC8cajXXo/wNezS31Glj5GcqrA==", - "dev": true, - "dependencies": { - "@csstools/selector-specificity": "^2.0.2", - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-color-function": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-2.2.3.tgz", - "integrity": "sha512-b1ptNkr1UWP96EEHqKBWWaV5m/0hgYGctgA/RVZhONeP1L3T/8hwoqDm9bB23yVCfOgE9U93KI9j06+pEkJTvw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-color-parser": "^1.2.0", - "@csstools/css-parser-algorithms": "^2.1.1", - "@csstools/css-tokenizer": "^2.1.1", - "@csstools/postcss-progressive-custom-properties": "^2.3.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-color-mix-function": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-1.0.3.tgz", - "integrity": "sha512-QGXjGugTluqFZWzVf+S3wCiRiI0ukXlYqCi7OnpDotP/zaVTyl/aqZujLFzTOXy24BoWnu89frGMc79ohY5eog==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-color-parser": "^1.2.0", - "@csstools/css-parser-algorithms": "^2.1.1", - "@csstools/css-tokenizer": "^2.1.1", - "@csstools/postcss-progressive-custom-properties": "^2.3.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-font-format-keywords": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-2.0.2.tgz", - "integrity": "sha512-iKYZlIs6JsNT7NKyRjyIyezTCHLh4L4BBB3F5Nx7Dc4Z/QmBgX+YJFuUSar8IM6KclGiAUFGomXFdYxAwJydlA==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-gradients-interpolation-method": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-3.0.6.tgz", - "integrity": "sha512-rBOBTat/YMmB0G8VHwKqDEx+RZ4KCU9j42K8LwS0IpZnyThalZZF7BCSsZ6TFlZhcRZKlZy3LLFI2pLqjNVGGA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-color-parser": "^1.2.0", - "@csstools/css-parser-algorithms": "^2.1.1", - "@csstools/css-tokenizer": "^2.1.1", - "@csstools/postcss-progressive-custom-properties": "^2.3.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-hwb-function": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-2.2.2.tgz", - "integrity": "sha512-W5Y5oaJ382HSlbdGfPf60d7dAK6Hqf10+Be1yZbd/TNNrQ/3dDdV1c07YwOXPQ3PZ6dvFMhxbIbn8EC3ki3nEg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-color-parser": "^1.2.0", - "@csstools/css-parser-algorithms": "^2.1.1", - "@csstools/css-tokenizer": "^2.1.1" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-ic-unit": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-2.0.4.tgz", - "integrity": "sha512-9W2ZbV7whWnr1Gt4qYgxMWzbevZMOvclUczT5vk4yR6vS53W/njiiUhtm/jh/BKYwQ1W3PECZjgAd2dH4ebJig==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^2.3.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-is-pseudo-class": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-3.2.1.tgz", - "integrity": "sha512-AtANdV34kJl04Al62is3eQRk/BfOfyAvEmRJvbt+nx5REqImLC+2XhuE6skgkcPli1l8ONS67wS+l1sBzySc3Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/selector-specificity": "^2.0.0", - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-logical-float-and-clear": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-float-and-clear/-/postcss-logical-float-and-clear-1.0.1.tgz", - "integrity": "sha512-eO9z2sMLddvlfFEW5Fxbjyd03zaO7cJafDurK4rCqyRt9P7aaWwha0LcSzoROlcZrw1NBV2JAp2vMKfPMQO1xw==", - "dev": true, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-logical-resize": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-resize/-/postcss-logical-resize-1.0.1.tgz", - "integrity": "sha512-x1ge74eCSvpBkDDWppl+7FuD2dL68WP+wwP2qvdUcKY17vJksz+XoE1ZRV38uJgS6FNUwC0AxrPW5gy3MxsDHQ==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-logical-viewport-units": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-1.0.3.tgz", - "integrity": "sha512-6zqcyRg9HSqIHIPMYdt6THWhRmE5/tyHKJQLysn2TeDf/ftq7Em9qwMTx98t2C/7UxIsYS8lOiHHxAVjWn2WUg==", - "dev": true, - "dependencies": { - "@csstools/css-tokenizer": "^2.1.1" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-media-minmax": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-1.0.4.tgz", - "integrity": "sha512-olnKTQk9+RMzpIpkjv55d44L4Ni02j8ZJoedJezQC5M03a56npcM1hx0apaTRG4Fz1wfPCQ0DBjQ8zsiJFelmA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-calc": "^1.1.1", - "@csstools/css-parser-algorithms": "^2.2.0", - "@csstools/css-tokenizer": "^2.1.1", - "@csstools/media-query-list-parser": "^2.1.1" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-media-queries-aspect-ratio-number-values": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-1.0.4.tgz", - "integrity": "sha512-IwyTbyR8E2y3kh6Fhrs251KjKBJeUPV5GlnUKnpU70PRFEN2DolWbf2V4+o/B9+Oj77P/DullLTulWEQ8uFtAA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-parser-algorithms": "^2.2.0", - "@csstools/css-tokenizer": "^2.1.1", - "@csstools/media-query-list-parser": "^2.1.1" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-nested-calc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-2.0.2.tgz", - "integrity": "sha512-jbwrP8rN4e7LNaRcpx3xpMUjhtt34I9OV+zgbcsYAAk6k1+3kODXJBf95/JMYWhu9g1oif7r06QVUgfWsKxCFw==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-normalize-display-values": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-2.0.1.tgz", - "integrity": "sha512-TQT5g3JQ5gPXC239YuRK8jFceXF9d25ZvBkyjzBGGoW5st5sPXFVQS8OjYb9IJ/K3CdfK4528y483cgS2DJR/w==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-oklab-function": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-2.2.3.tgz", - "integrity": "sha512-AgJ2rWMnLCDcbSMTHSqBYn66DNLBym6JpBpCaqmwZ9huGdljjDRuH3DzOYzkgQ7Pm2K92IYIq54IvFHloUOdvA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-color-parser": "^1.2.0", - "@csstools/css-parser-algorithms": "^2.1.1", - "@csstools/css-tokenizer": "^2.1.1", - "@csstools/postcss-progressive-custom-properties": "^2.3.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-progressive-custom-properties": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-2.3.0.tgz", - "integrity": "sha512-Zd8ojyMlsL919TBExQ1I0CTpBDdyCpH/yOdqatZpuC3sd22K4SwC7+Yez3Q/vmXMWSAl+shjNeFZ7JMyxMjK+Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-relative-color-syntax": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-1.0.2.tgz", - "integrity": "sha512-juCoVInkgH2TZPfOhyx6tIal7jW37L/0Tt+Vcl1LoxqQA9sxcg3JWYZ98pl1BonDnki6s/M7nXzFQHWsWMeHgw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-color-parser": "^1.2.0", - "@csstools/css-parser-algorithms": "^2.1.1", - "@csstools/css-tokenizer": "^2.1.1", - "@csstools/postcss-progressive-custom-properties": "^2.3.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-scope-pseudo-class": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-scope-pseudo-class/-/postcss-scope-pseudo-class-2.0.2.tgz", - "integrity": "sha512-6Pvo4uexUCXt+Hz5iUtemQAcIuCYnL+ePs1khFR6/xPgC92aQLJ0zGHonWoewiBE+I++4gXK3pr+R1rlOFHe5w==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-stepped-value-functions": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-2.1.1.tgz", - "integrity": "sha512-YCvdF0GCZK35nhLgs7ippcxDlRVe5QsSht3+EghqTjnYnyl3BbWIN6fYQ1dKWYTJ+7Bgi41TgqQFfJDcp9Xy/w==", - "dev": true, - "dependencies": { - "@csstools/css-calc": "^1.1.1", - "@csstools/css-parser-algorithms": "^2.1.1", - "@csstools/css-tokenizer": "^2.1.1" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-text-decoration-shorthand": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-2.2.4.tgz", - "integrity": "sha512-zPN56sQkS/7YTCVZhOBVCWf7AiNge8fXDl7JVaHLz2RyT4pnyK2gFjckWRLpO0A2xkm1lCgZ0bepYZTwAVd/5A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/color-helpers": "^2.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-trigonometric-functions": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-2.1.1.tgz", - "integrity": "sha512-XcXmHEFfHXhvYz40FtDlA4Fp4NQln2bWTsCwthd2c+MCnYArUYU3YaMqzR5CrKP3pMoGYTBnp5fMqf1HxItNyw==", - "dev": true, - "dependencies": { - "@csstools/css-calc": "^1.1.1", - "@csstools/css-parser-algorithms": "^2.1.1", - "@csstools/css-tokenizer": "^2.1.1" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/postcss-unset-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-2.0.1.tgz", - "integrity": "sha512-oJ9Xl29/yU8U7/pnMJRqAZd4YXNCfGEdcP4ywREuqm/xMqcgDNDppYRoCGDt40aaZQIEKBS79LytUDN/DHf0Ew==", - "dev": true, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/@csstools/selector-specificity": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz", - "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", - "dev": true, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss-selector-parser": "^6.0.10" - } - }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", - "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", - "dev": true, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@electron-forge/cli": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/cli/-/cli-6.4.2.tgz", - "integrity": "sha512-bM6YVTV0uUEpIL1jkpARlSm4Li26XZn+avC/lyTdpPqnd65T/oXZNkrAD+2Jb0RlgplOaM21qWm7ybtvKDGDyA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-.electron-forge-cli?utm_medium=referral&utm_source=npm_fund" - } - ], - "dependencies": { - "@electron-forge/core": "6.4.2", - "@electron-forge/shared-types": "6.4.2", - "@electron/get": "^2.0.0", - "chalk": "^4.0.0", - "commander": "^4.1.1", - "debug": "^4.3.1", - "fs-extra": "^10.0.0", - "listr2": "^5.0.3", - "semver": "^7.2.1" - }, - "bin": { - "electron-forge": "dist/electron-forge.js", - "electron-forge-vscode-nix": "script/vscode.sh", - "electron-forge-vscode-win": "script/vscode.cmd" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/cli/node_modules/@electron-forge/shared-types": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-6.4.2.tgz", - "integrity": "sha512-DKOUMsdTXZIq8XiqY0Hi3C+dam/JKUnvfBjwcUeyZqPdgEE1qry8xZmmjorXuLrRf1Jq8rhxYGQInSK4af0QYw==", - "dev": true, - "dependencies": { - "@electron/rebuild": "^3.2.10", - "electron-packager": "^17.1.2", - "listr2": "^5.0.3" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/cli/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@electron-forge/core": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/core/-/core-6.4.2.tgz", - "integrity": "sha512-VtrFZ1Q7NG1ov0jJO/tUvUiYdWZ0Y31xw762is/jfpRPD6V/soOpwJJAoWoPK9TZVkTm2pkS8S5LikCMbNCLxw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-.electron-forge-core?utm_medium=referral&utm_source=npm_fund" - } - ], - "dependencies": { - "@electron-forge/core-utils": "6.4.2", - "@electron-forge/maker-base": "6.4.2", - "@electron-forge/plugin-base": "6.4.2", - "@electron-forge/publisher-base": "6.4.2", - "@electron-forge/shared-types": "6.4.2", - "@electron-forge/template-base": "6.4.2", - "@electron-forge/template-vite": "6.4.2", - "@electron-forge/template-vite-typescript": "6.4.2", - "@electron-forge/template-webpack": "6.4.2", - "@electron-forge/template-webpack-typescript": "6.4.2", - "@electron/get": "^2.0.0", - "@electron/rebuild": "^3.2.10", - "@malept/cross-spawn-promise": "^2.0.0", - "chalk": "^4.0.0", - "debug": "^4.3.1", - "electron-packager": "^17.1.2", - "fast-glob": "^3.2.7", - "filenamify": "^4.1.0", - "find-up": "^5.0.0", - "fs-extra": "^10.0.0", - "got": "^11.8.5", - "interpret": "^3.1.1", - "listr2": "^5.0.3", - "lodash": "^4.17.20", - "log-symbols": "^4.0.0", - "node-fetch": "^2.6.7", - "progress": "^2.0.3", - "rechoir": "^0.8.0", - "resolve-package": "^1.0.1", - "semver": "^7.2.1", - "source-map-support": "^0.5.13", - "sudo-prompt": "^9.1.1", - "username": "^5.1.0", - "yarn-or-npm": "^3.0.1" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/core-utils": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/core-utils/-/core-utils-6.4.2.tgz", - "integrity": "sha512-CjB3aakmRsXAMMDYc8PxNTMf4FdI29y4PErfv7eCXlL5oo3JW0VSKZIV7R8/Po0S0got85q2kmhZgCKuxL1BNA==", - "dev": true, - "dependencies": { - "@electron-forge/shared-types": "6.4.2", - "@electron/rebuild": "^3.2.10", - "@malept/cross-spawn-promise": "^2.0.0", - "chalk": "^4.0.0", - "debug": "^4.3.1", - "find-up": "^5.0.0", - "fs-extra": "^10.0.0", - "log-symbols": "^4.0.0", - "semver": "^7.2.1", - "yarn-or-npm": "^3.0.1" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/core-utils/node_modules/@electron-forge/shared-types": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-6.4.2.tgz", - "integrity": "sha512-DKOUMsdTXZIq8XiqY0Hi3C+dam/JKUnvfBjwcUeyZqPdgEE1qry8xZmmjorXuLrRf1Jq8rhxYGQInSK4af0QYw==", - "dev": true, - "dependencies": { - "@electron/rebuild": "^3.2.10", - "electron-packager": "^17.1.2", - "listr2": "^5.0.3" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/core/node_modules/@electron-forge/maker-base": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/maker-base/-/maker-base-6.4.2.tgz", - "integrity": "sha512-zW3GH+LqDK9nxQmQEFkJPR8RqiX0lVk6a4mXll3ngujN1fPevO4ivUAWmaEVeC1dH/hXbN7s9m0S6a37MigftQ==", - "dev": true, - "dependencies": { - "@electron-forge/shared-types": "6.4.2", - "fs-extra": "^10.0.0", - "which": "^2.0.2" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/core/node_modules/@electron-forge/shared-types": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-6.4.2.tgz", - "integrity": "sha512-DKOUMsdTXZIq8XiqY0Hi3C+dam/JKUnvfBjwcUeyZqPdgEE1qry8xZmmjorXuLrRf1Jq8rhxYGQInSK4af0QYw==", - "dev": true, - "dependencies": { - "@electron/rebuild": "^3.2.10", - "electron-packager": "^17.1.2", - "listr2": "^5.0.3" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/maker-base": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@electron-forge/maker-base/-/maker-base-6.2.1.tgz", - "integrity": "sha512-LnvGtTJ/RNojKdUKktYEcbLqPggXdMBs1uscQRgXkI3XnVGdEi+/j5+Eg5ka4d6FnsaUkz//U5yhPtNFhDbNSw==", - "dev": true, - "dependencies": { - "@electron-forge/shared-types": "6.2.1", - "fs-extra": "^10.0.0", - "which": "^2.0.2" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/maker-deb": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/maker-deb/-/maker-deb-6.4.2.tgz", - "integrity": "sha512-tlV8ffivgBP94vtYXgAeXgzeKCaRyLuWH9LT8PQW1QrYbAFpCMmuwk/zFaJkyMklImCWmDFTPYMEqdEJGd7Npg==", - "dev": true, - "dependencies": { - "@electron-forge/maker-base": "6.4.2", - "@electron-forge/shared-types": "6.4.2" - }, - "engines": { - "node": ">= 14.17.5" - }, - "optionalDependencies": { - "electron-installer-debian": "^3.2.0" - } - }, - "node_modules/@electron-forge/maker-deb/node_modules/@electron-forge/maker-base": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/maker-base/-/maker-base-6.4.2.tgz", - "integrity": "sha512-zW3GH+LqDK9nxQmQEFkJPR8RqiX0lVk6a4mXll3ngujN1fPevO4ivUAWmaEVeC1dH/hXbN7s9m0S6a37MigftQ==", - "dev": true, - "dependencies": { - "@electron-forge/shared-types": "6.4.2", - "fs-extra": "^10.0.0", - "which": "^2.0.2" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/maker-deb/node_modules/@electron-forge/shared-types": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-6.4.2.tgz", - "integrity": "sha512-DKOUMsdTXZIq8XiqY0Hi3C+dam/JKUnvfBjwcUeyZqPdgEE1qry8xZmmjorXuLrRf1Jq8rhxYGQInSK4af0QYw==", - "dev": true, - "dependencies": { - "@electron/rebuild": "^3.2.10", - "electron-packager": "^17.1.2", - "listr2": "^5.0.3" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/maker-rpm": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/maker-rpm/-/maker-rpm-6.4.2.tgz", - "integrity": "sha512-+hfbY5pYbAer0y07OtOzVgVBHoTRmemqqZ//T0mKJpyK2ThHKGTvyW8FFlr5jlQs5LoDCM2WHKE8oGtRhivsMg==", - "dev": true, - "dependencies": { - "@electron-forge/maker-base": "6.4.2", - "@electron-forge/shared-types": "6.4.2" - }, - "engines": { - "node": ">= 14.17.5" - }, - "optionalDependencies": { - "electron-installer-redhat": "^3.2.0" - } - }, - "node_modules/@electron-forge/maker-rpm/node_modules/@electron-forge/maker-base": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/maker-base/-/maker-base-6.4.2.tgz", - "integrity": "sha512-zW3GH+LqDK9nxQmQEFkJPR8RqiX0lVk6a4mXll3ngujN1fPevO4ivUAWmaEVeC1dH/hXbN7s9m0S6a37MigftQ==", - "dev": true, - "dependencies": { - "@electron-forge/shared-types": "6.4.2", - "fs-extra": "^10.0.0", - "which": "^2.0.2" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/maker-rpm/node_modules/@electron-forge/shared-types": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-6.4.2.tgz", - "integrity": "sha512-DKOUMsdTXZIq8XiqY0Hi3C+dam/JKUnvfBjwcUeyZqPdgEE1qry8xZmmjorXuLrRf1Jq8rhxYGQInSK4af0QYw==", - "dev": true, - "dependencies": { - "@electron/rebuild": "^3.2.10", - "electron-packager": "^17.1.2", - "listr2": "^5.0.3" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/maker-squirrel": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/maker-squirrel/-/maker-squirrel-6.4.2.tgz", - "integrity": "sha512-ukK3RcFaBrQXUzR52PsHxfwDq5XKSnj6A1kkXiyHWqgj+HIU97prBScBb5JRtasPvYN+nDdQO2vlInsLaqcx9Q==", - "dev": true, - "dependencies": { - "@electron-forge/maker-base": "6.4.2", - "@electron-forge/shared-types": "6.4.2", - "fs-extra": "^10.0.0" - }, - "engines": { - "node": ">= 14.17.5" - }, - "optionalDependencies": { - "electron-winstaller": "^5.0.0" - } - }, - "node_modules/@electron-forge/maker-squirrel/node_modules/@electron-forge/maker-base": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/maker-base/-/maker-base-6.4.2.tgz", - "integrity": "sha512-zW3GH+LqDK9nxQmQEFkJPR8RqiX0lVk6a4mXll3ngujN1fPevO4ivUAWmaEVeC1dH/hXbN7s9m0S6a37MigftQ==", - "dev": true, - "dependencies": { - "@electron-forge/shared-types": "6.4.2", - "fs-extra": "^10.0.0", - "which": "^2.0.2" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/maker-squirrel/node_modules/@electron-forge/shared-types": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-6.4.2.tgz", - "integrity": "sha512-DKOUMsdTXZIq8XiqY0Hi3C+dam/JKUnvfBjwcUeyZqPdgEE1qry8xZmmjorXuLrRf1Jq8rhxYGQInSK4af0QYw==", - "dev": true, - "dependencies": { - "@electron/rebuild": "^3.2.10", - "electron-packager": "^17.1.2", - "listr2": "^5.0.3" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/maker-zip": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@electron-forge/maker-zip/-/maker-zip-6.2.1.tgz", - "integrity": "sha512-EgKArMT3Njn9/NZ7g2gGrhl8Y3F84Mm9b9Yt5WOziQaUAWvywFdijhUn4Oq631f3wU93xNq/CZbKvYWAK0NjnA==", - "dev": true, - "dependencies": { - "@electron-forge/maker-base": "6.2.1", - "@electron-forge/shared-types": "6.2.1", - "cross-zip": "^4.0.0", - "fs-extra": "^10.0.0", - "got": "^11.8.5" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/plugin-auto-unpack-natives": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/plugin-auto-unpack-natives/-/plugin-auto-unpack-natives-6.4.2.tgz", - "integrity": "sha512-AXmPQc2nUFuh/xFC+Qsebg/eg3M+5GQV6MOzMvTBZ9N1w49XtZbtvEGAdISnZWERExNRcwH+j+zuSAGtm6Y2Yw==", - "dev": true, - "dependencies": { - "@electron-forge/plugin-base": "6.4.2", - "@electron-forge/shared-types": "6.4.2" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/plugin-auto-unpack-natives/node_modules/@electron-forge/shared-types": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-6.4.2.tgz", - "integrity": "sha512-DKOUMsdTXZIq8XiqY0Hi3C+dam/JKUnvfBjwcUeyZqPdgEE1qry8xZmmjorXuLrRf1Jq8rhxYGQInSK4af0QYw==", - "dev": true, - "dependencies": { - "@electron/rebuild": "^3.2.10", - "electron-packager": "^17.1.2", - "listr2": "^5.0.3" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/plugin-base": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/plugin-base/-/plugin-base-6.4.2.tgz", - "integrity": "sha512-g6AAtQ7fZ94djBmwcnWasQ8xgaNVNjgaQ00GLK0NkmQ7n0PNbsnlMDuw9vdfTiL6WaLg5nxNSYc9bFJP/rtyeA==", - "dev": true, - "dependencies": { - "@electron-forge/shared-types": "6.4.2" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/plugin-base/node_modules/@electron-forge/shared-types": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-6.4.2.tgz", - "integrity": "sha512-DKOUMsdTXZIq8XiqY0Hi3C+dam/JKUnvfBjwcUeyZqPdgEE1qry8xZmmjorXuLrRf1Jq8rhxYGQInSK4af0QYw==", - "dev": true, - "dependencies": { - "@electron/rebuild": "^3.2.10", - "electron-packager": "^17.1.2", - "listr2": "^5.0.3" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/plugin-webpack": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/plugin-webpack/-/plugin-webpack-6.4.2.tgz", - "integrity": "sha512-o+iQajtCCnYEPaJy0IkYI0noGqHoCDGMydki9sNU+CoUfDYBCNDkUXLTpoVkL6GmKTINT8kvOFgsFfPF9fhXlg==", - "dev": true, - "dependencies": { - "@electron-forge/core-utils": "6.4.2", - "@electron-forge/plugin-base": "6.4.2", - "@electron-forge/shared-types": "6.4.2", - "@electron-forge/web-multi-logger": "6.4.2", - "chalk": "^4.0.0", - "debug": "^4.3.1", - "fs-extra": "^10.0.0", - "html-webpack-plugin": "^5.5.3", - "webpack": "^5.69.1", - "webpack-dev-server": "^4.0.0", - "webpack-merge": "^5.7.3" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/plugin-webpack/node_modules/@electron-forge/shared-types": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-6.4.2.tgz", - "integrity": "sha512-DKOUMsdTXZIq8XiqY0Hi3C+dam/JKUnvfBjwcUeyZqPdgEE1qry8xZmmjorXuLrRf1Jq8rhxYGQInSK4af0QYw==", - "dev": true, - "dependencies": { - "@electron/rebuild": "^3.2.10", - "electron-packager": "^17.1.2", - "listr2": "^5.0.3" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/publisher-base": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/publisher-base/-/publisher-base-6.4.2.tgz", - "integrity": "sha512-Tnf9O8MFzdT1gsb5EDDaQUoslt7gUuUywtsr+lT/fpBlBQbei2fvioTwvZ1Q1cmsKnld7XhRh6unfgdWLTZzgw==", - "dev": true, - "dependencies": { - "@electron-forge/shared-types": "6.4.2" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/publisher-base/node_modules/@electron-forge/shared-types": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-6.4.2.tgz", - "integrity": "sha512-DKOUMsdTXZIq8XiqY0Hi3C+dam/JKUnvfBjwcUeyZqPdgEE1qry8xZmmjorXuLrRf1Jq8rhxYGQInSK4af0QYw==", - "dev": true, - "dependencies": { - "@electron/rebuild": "^3.2.10", - "electron-packager": "^17.1.2", - "listr2": "^5.0.3" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/publisher-github": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/publisher-github/-/publisher-github-6.4.2.tgz", - "integrity": "sha512-SUb2JxU8hHx8vbc1UA97lcs3ChyDeiTzx8Q5/DtooC/2sSqD85bct5ewLaANRXK/58cBVpyHMgo4uiq7mlyQWg==", - "dev": true, - "dependencies": { - "@electron-forge/publisher-base": "6.4.2", - "@electron-forge/shared-types": "6.4.2", - "@octokit/core": "^3.2.4", - "@octokit/plugin-retry": "^3.0.9", - "@octokit/rest": "^18.0.11", - "@octokit/types": "^6.1.2", - "debug": "^4.3.1", - "fs-extra": "^10.0.0", - "mime-types": "^2.1.25" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/publisher-github/node_modules/@electron-forge/shared-types": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-6.4.2.tgz", - "integrity": "sha512-DKOUMsdTXZIq8XiqY0Hi3C+dam/JKUnvfBjwcUeyZqPdgEE1qry8xZmmjorXuLrRf1Jq8rhxYGQInSK4af0QYw==", - "dev": true, - "dependencies": { - "@electron/rebuild": "^3.2.10", - "electron-packager": "^17.1.2", - "listr2": "^5.0.3" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/shared-types": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-6.2.1.tgz", - "integrity": "sha512-kLazG5XUAqb3Duyhq7XyGluINRwCQRaIiuvHwlvnZYYu6NZQTz9xUm6tQ9v05EtFblUx2iRjY67DJRZSt3dzTQ==", - "dev": true, - "dependencies": { - "@electron/rebuild": "^3.2.10", - "electron-packager": "^17.1.1", - "listr2": "^5.0.3" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/template-base": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/template-base/-/template-base-6.4.2.tgz", - "integrity": "sha512-vsQh+64Fr2Vxg6k8DAahWq4MAdB2F2qTig+LgIJENv8ksbzC1YIq05SBAS/g2674cdr7WdwyukMy2rgxe3rhnQ==", - "dev": true, - "dependencies": { - "@electron-forge/shared-types": "6.4.2", - "@malept/cross-spawn-promise": "^2.0.0", - "debug": "^4.3.1", - "fs-extra": "^10.0.0", - "username": "^5.1.0" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/template-base/node_modules/@electron-forge/shared-types": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-6.4.2.tgz", - "integrity": "sha512-DKOUMsdTXZIq8XiqY0Hi3C+dam/JKUnvfBjwcUeyZqPdgEE1qry8xZmmjorXuLrRf1Jq8rhxYGQInSK4af0QYw==", - "dev": true, - "dependencies": { - "@electron/rebuild": "^3.2.10", - "electron-packager": "^17.1.2", - "listr2": "^5.0.3" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/template-vite": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/template-vite/-/template-vite-6.4.2.tgz", - "integrity": "sha512-NX7jHRblBmIqufMbqWgpI/VnpgF/qMSTq9ZPmDSXamBhid336MC6+DoWzDpXceQZEp0m/jpMLR04ynr8O4jGlg==", - "dev": true, - "dependencies": { - "@electron-forge/shared-types": "6.4.2", - "@electron-forge/template-base": "6.4.2", - "fs-extra": "^10.0.0" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - } - }, - "node_modules/@electron-forge/template-vite-typescript": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/template-vite-typescript/-/template-vite-typescript-6.4.2.tgz", - "integrity": "sha512-h3pn6onvC/nLglmJuelYU82Qzrh0l6MqvbBGoT39bbDoRLIqmlhWTWppHgDJVXAGrSoH+9BEpptipeBQWirFwg==", - "dev": true, - "dependencies": { - "@electron-forge/shared-types": "6.4.2", - "@electron-forge/template-base": "6.4.2", - "fs-extra": "^10.0.0" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - } - }, - "node_modules/@electron-forge/template-vite-typescript/node_modules/@electron-forge/shared-types": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-6.4.2.tgz", - "integrity": "sha512-DKOUMsdTXZIq8XiqY0Hi3C+dam/JKUnvfBjwcUeyZqPdgEE1qry8xZmmjorXuLrRf1Jq8rhxYGQInSK4af0QYw==", - "dev": true, - "dependencies": { - "@electron/rebuild": "^3.2.10", - "electron-packager": "^17.1.2", - "listr2": "^5.0.3" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/template-vite/node_modules/@electron-forge/shared-types": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-6.4.2.tgz", - "integrity": "sha512-DKOUMsdTXZIq8XiqY0Hi3C+dam/JKUnvfBjwcUeyZqPdgEE1qry8xZmmjorXuLrRf1Jq8rhxYGQInSK4af0QYw==", - "dev": true, - "dependencies": { - "@electron/rebuild": "^3.2.10", - "electron-packager": "^17.1.2", - "listr2": "^5.0.3" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/template-webpack": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/template-webpack/-/template-webpack-6.4.2.tgz", - "integrity": "sha512-9QYr/td4cmnGOj8UF25W6An/eI+JXj9T/b+KFybL3cQ87H1yrQOn2T84Bm5/JaB4SPdIu4FdKRjqwR7C7R0g2w==", - "dev": true, - "dependencies": { - "@electron-forge/shared-types": "6.4.2", - "@electron-forge/template-base": "6.4.2", - "fs-extra": "^10.0.0" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/template-webpack-typescript": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/template-webpack-typescript/-/template-webpack-typescript-6.4.2.tgz", - "integrity": "sha512-MPAZQ4v6piCED7NT1LTVQf61o6Eg/laNoKbhbrFBSH1i20OUwbtV2MLj6Op292ynI9+1qdHKmFgctr6qPTCAQw==", - "dev": true, - "dependencies": { - "@electron-forge/shared-types": "6.4.2", - "@electron-forge/template-base": "6.4.2", - "fs-extra": "^10.0.0" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/template-webpack-typescript/node_modules/@electron-forge/shared-types": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-6.4.2.tgz", - "integrity": "sha512-DKOUMsdTXZIq8XiqY0Hi3C+dam/JKUnvfBjwcUeyZqPdgEE1qry8xZmmjorXuLrRf1Jq8rhxYGQInSK4af0QYw==", - "dev": true, - "dependencies": { - "@electron/rebuild": "^3.2.10", - "electron-packager": "^17.1.2", - "listr2": "^5.0.3" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/template-webpack/node_modules/@electron-forge/shared-types": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-6.4.2.tgz", - "integrity": "sha512-DKOUMsdTXZIq8XiqY0Hi3C+dam/JKUnvfBjwcUeyZqPdgEE1qry8xZmmjorXuLrRf1Jq8rhxYGQInSK4af0QYw==", - "dev": true, - "dependencies": { - "@electron/rebuild": "^3.2.10", - "electron-packager": "^17.1.2", - "listr2": "^5.0.3" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron-forge/web-multi-logger": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@electron-forge/web-multi-logger/-/web-multi-logger-6.4.2.tgz", - "integrity": "sha512-acZwr5+4l5G6baaqUwU9tuJ/njhJLUu9LgTvjedknIipg22EwLqwhjdXuTpWb9gidXDjdAjSRFzEEyVZCCooFA==", - "dev": true, - "dependencies": { - "express": "^4.17.1", - "express-ws": "^5.0.2", - "xterm": "^4.9.0", - "xterm-addon-fit": "^0.5.0", - "xterm-addon-search": "^0.8.0" - }, - "engines": { - "node": ">= 14.17.5" - } - }, - "node_modules/@electron/asar": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.4.tgz", - "integrity": "sha512-lykfY3TJRRWFeTxccEKdf1I6BLl2Plw81H0bbp4Fc5iEc67foDCa5pjJQULVgo0wF+Dli75f3xVcdb/67FFZ/g==", - "dev": true, - "dependencies": { - "chromium-pickle-js": "^0.2.0", - "commander": "^5.0.0", - "glob": "^7.1.6", - "minimatch": "^3.0.4" - }, - "bin": { - "asar": "bin/asar.js" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/@electron/get": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.2.tgz", - "integrity": "sha512-eFZVFoRXb3GFGd7Ak7W4+6jBl9wBtiZ4AaYOse97ej6mKj5tkyO0dUnUChs1IhJZtx1BENo4/p4WUTXpi6vT+g==", - "dependencies": { - "debug": "^4.1.1", - "env-paths": "^2.2.0", - "fs-extra": "^8.1.0", - "got": "^11.8.5", - "progress": "^2.0.3", - "semver": "^6.2.0", - "sumchecker": "^3.0.1" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "global-agent": "^3.0.0" - } - }, - "node_modules/@electron/get/node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/@electron/get/node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/get/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@electron/get/node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/@electron/notarize": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-1.2.4.tgz", - "integrity": "sha512-W5GQhJEosFNafewnS28d3bpQ37/s91CDWqxVchHfmv2dQSTWpOzNlUVQwYzC1ay5bChRV/A9BTL68yj0Pa+TSg==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "fs-extra": "^9.0.1" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron/notarize/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@electron/osx-sign": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.0.5.tgz", - "integrity": "sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww==", - "dev": true, - "dependencies": { - "compare-version": "^0.1.2", - "debug": "^4.3.4", - "fs-extra": "^10.0.0", - "isbinaryfile": "^4.0.8", - "minimist": "^1.2.6", - "plist": "^3.0.5" - }, - "bin": { - "electron-osx-flat": "bin/electron-osx-flat.js", - "electron-osx-sign": "bin/electron-osx-sign.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@electron/rebuild": { - "version": "3.2.13", - "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.2.13.tgz", - "integrity": "sha512-DH9Ol4JCnHDYVOD0fKWq+Qqbn/0WU1O6QR0mIpMXEVU4YFM4PlaqNC9K36mGShNBxxGFotZCMDrB1wl/iHM12g==", - "dev": true, - "dependencies": { - "@malept/cross-spawn-promise": "^2.0.0", - "chalk": "^4.0.0", - "debug": "^4.1.1", - "detect-libc": "^2.0.1", - "fs-extra": "^10.0.0", - "got": "^11.7.0", - "node-abi": "^3.0.0", - "node-api-version": "^0.1.4", - "node-gyp": "^9.0.0", - "ora": "^5.1.0", - "semver": "^7.3.5", - "tar": "^6.0.5", - "yargs": "^17.0.1" - }, - "bin": { - "electron-rebuild": "lib/cli.js" - }, - "engines": { - "node": ">=12.13.0" - } - }, - "node_modules/@electron/remote": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.0.10.tgz", - "integrity": "sha512-3SFKKaQXcyWgwmibud+UqJl/XlHOgLcI3fwtB9pNelPSJAcTxocOJrF6FaxBIQaj1+R05Di6xuAswZpXAW7xhA==", - "peerDependencies": { - "electron": ">= 13.0.0" - } - }, - "node_modules/@electron/universal": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.4.1.tgz", - "integrity": "sha512-lE/U3UNw1YHuowNbTmKNs9UlS3En3cPgwM5MI+agIgr/B1hSze9NdOP0qn7boZaI9Lph8IDv3/24g9IxnJP7aQ==", - "dev": true, - "dependencies": { - "@electron/asar": "^3.2.1", - "@malept/cross-spawn-promise": "^1.1.0", - "debug": "^4.3.1", - "dir-compare": "^3.0.0", - "fs-extra": "^9.0.1", - "minimatch": "^3.0.4", - "plist": "^3.0.4" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/@electron/universal/node_modules/@malept/cross-spawn-promise": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", - "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" - } - ], - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@electron/universal/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", - "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.5.2", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", - "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@heroicons/react": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.0.18.tgz", - "integrity": "sha512-7TyMjRrZZMBPa+/5Y8lN0iyvUU/01PeMGX2+RE7cQWpEUIcb4QotzUObFkJDejj/HUH4qjP/eQ0gzzKs2f+6Yw==", - "peerDependencies": { - "react": ">= 16" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", - "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", - "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", - "dev": true - }, - "node_modules/@lukeed/csprng": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", - "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@lukeed/uuid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@lukeed/uuid/-/uuid-2.0.1.tgz", - "integrity": "sha512-qC72D4+CDdjGqJvkFMMEAtancHUQ7/d/tAiHf64z8MopFDmcrtbcJuerDtFceuAfQJ2pDSfCKCtbqoGBNnwg0w==", - "dependencies": { - "@lukeed/csprng": "^1.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@malept/cross-spawn-promise": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", - "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" - } - ], - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/@nicolo-ribaudo/semver-v6": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz", - "integrity": "sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@npmcli/fs": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz", - "integrity": "sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==", - "dev": true, - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@octokit/auth-token": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", - "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", - "dev": true, - "dependencies": { - "@octokit/types": "^6.0.3" - } - }, - "node_modules/@octokit/core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", - "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", - "dev": true, - "dependencies": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.3", - "@octokit/request-error": "^2.0.5", - "@octokit/types": "^6.0.3", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/endpoint": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", - "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", - "dev": true, - "dependencies": { - "@octokit/types": "^6.0.3", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/endpoint/node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@octokit/graphql": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", - "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", - "dev": true, - "dependencies": { - "@octokit/request": "^5.6.0", - "@octokit/types": "^6.0.3", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==", - "dev": true - }, - "node_modules/@octokit/plugin-paginate-rest": { - "version": "2.21.3", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", - "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", - "dev": true, - "dependencies": { - "@octokit/types": "^6.40.0" - }, - "peerDependencies": { - "@octokit/core": ">=2" - } - }, - "node_modules/@octokit/plugin-request-log": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", - "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", - "dev": true, - "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "5.16.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", - "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", - "dev": true, - "dependencies": { - "@octokit/types": "^6.39.0", - "deprecation": "^2.3.1" - }, - "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/@octokit/plugin-retry": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-3.0.9.tgz", - "integrity": "sha512-r+fArdP5+TG6l1Rv/C9hVoty6tldw6cE2pRHNGmFPdyfrc696R6JjrQ3d7HdVqGwuzfyrcaLAKD7K8TX8aehUQ==", - "dev": true, - "dependencies": { - "@octokit/types": "^6.0.3", - "bottleneck": "^2.15.3" - } - }, - "node_modules/@octokit/request": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", - "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", - "dev": true, - "dependencies": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.16.1", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "dev": true, - "dependencies": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "node_modules/@octokit/request/node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@octokit/rest": { - "version": "18.12.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", - "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", - "dev": true, - "dependencies": { - "@octokit/core": "^3.5.1", - "@octokit/plugin-paginate-rest": "^2.16.8", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^5.12.0" - } - }, - "node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dev": true, - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@segment/analytics-core": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@segment/analytics-core/-/analytics-core-1.3.0.tgz", - "integrity": "sha512-ujScWZH49NK1hYlp2/EMw45nOPEh+pmTydAnR6gSkRNucZD4fuinvpPL03rmFCw8ibaMuKLAdgPJfQ0gkLKZ5A==", - "dependencies": { - "@lukeed/uuid": "^2.0.0", - "dset": "^3.1.2", - "tslib": "^2.4.1" - } - }, - "node_modules/@segment/analytics-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@segment/analytics-node/-/analytics-node-1.0.0.tgz", - "integrity": "sha512-UWFujSxRkRauZuMVF4MPOT5QPvX4i7kiC2QCsozHhltoTiR2SBWRI86cYO/JI/Uk7qKaOxxGFDkJarCyIP7uLA==", - "dependencies": { - "@lukeed/uuid": "^2.0.0", - "@segment/analytics-core": "1.3.0", - "buffer": "^6.0.3", - "node-fetch": "^2.6.7", - "tslib": "^2.4.1" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@segment/analytics-node/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@svgr/babel-plugin-add-jsx-attribute": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", - "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", - "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", - "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", - "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-svg-dynamic-title": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", - "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-svg-em-dimensions": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", - "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-transform-react-native-svg": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.0.0.tgz", - "integrity": "sha512-UKrY3860AQICgH7g+6h2zkoxeVEPLYwX/uAjmqo4PIq2FIHppwhIqZstIyTz0ZtlwreKR41O3W3BzsBBiJV2Aw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-transform-svg-component": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", - "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-preset": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.0.0.tgz", - "integrity": "sha512-KLcjiZychInVrhs86OvcYPLTFu9L5XV2vj0XAaE1HwE3J3jLmIzRY8ttdeAg/iFyp8nhavJpafpDZTt+1LIpkQ==", - "dev": true, - "dependencies": { - "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", - "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", - "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", - "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", - "@svgr/babel-plugin-transform-react-native-svg": "8.0.0", - "@svgr/babel-plugin-transform-svg-component": "8.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/core": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.0.0.tgz", - "integrity": "sha512-aJKtc+Pie/rFYsVH/unSkDaZGvEeylNv/s2cP+ta9/rYWxRVvoV/S4Qw65Kmrtah4CBK5PM6ISH9qUH7IJQCng==", - "dev": true, - "dependencies": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.0.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^8.1.3", - "snake-case": "^3.0.4" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/core/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@svgr/core/node_modules/cosmiconfig": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", - "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", - "dev": true, - "dependencies": { - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - } - }, - "node_modules/@svgr/hast-util-to-babel-ast": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", - "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", - "dev": true, - "dependencies": { - "@babel/types": "^7.21.3", - "entities": "^4.4.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/hast-util-to-babel-ast/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/@svgr/plugin-jsx": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.0.1.tgz", - "integrity": "sha512-bfCFb+4ZsM3UuKP2t7KmDwn6YV8qVn9HIQJmau6xeQb/iV65Rpi7NBNBWA2hcCd4GKoCqG8hpaaDk5FDR0eH+g==", - "dev": true, - "dependencies": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.0.0", - "@svgr/hast-util-to-babel-ast": "8.0.0", - "svg-parser": "^2.0.4" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@svgr/core": "*" - } - }, - "node_modules/@svgr/plugin-svgo": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.0.1.tgz", - "integrity": "sha512-29OJ1QmJgnohQHDAgAuY2h21xWD6TZiXji+hnx+W635RiXTAlHTbjrZDktfqzkN0bOeQEtNe+xgq73/XeWFfSg==", - "dev": true, - "dependencies": { - "cosmiconfig": "^8.1.3", - "deepmerge": "^4.3.1", - "svgo": "^3.0.2" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@svgr/core": "*" - } - }, - "node_modules/@svgr/plugin-svgo/node_modules/cosmiconfig": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", - "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", - "dev": true, - "dependencies": { - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - } - }, - "node_modules/@svgr/webpack": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.0.1.tgz", - "integrity": "sha512-zSoeKcbCmfMXjA11uDuCJb+1LWNb3vy6Qw/VHj0Nfcl3UuqwuoZWknHsBIhCWvi4wU9vPui3aq054qjVyZqY4A==", - "dev": true, - "dependencies": { - "@babel/core": "^7.21.3", - "@babel/plugin-transform-react-constant-elements": "^7.21.3", - "@babel/preset-env": "^7.20.2", - "@babel/preset-react": "^7.18.6", - "@babel/preset-typescript": "^7.21.0", - "@svgr/core": "8.0.0", - "@svgr/plugin-jsx": "8.0.1", - "@svgr/plugin-svgo": "8.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", - "dependencies": { - "defer-to-connect": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "dev": true, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "node_modules/@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dev": true, - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/bonjour": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", - "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/cacheable-request": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", - "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "^3.1.4", - "@types/node": "*", - "@types/responselike": "^1.0.0" - } - }, - "node_modules/@types/chmodr": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/chmodr/-/chmodr-1.0.0.tgz", - "integrity": "sha512-S+X+Gy8V1uijitezjzXuan5vHbjllKgnC6q4VrD30HF2WRF6oIwQ/Wfjzvn5tGIIsl4VtRnBQbzqyzBo02juhw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", - "integrity": "sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig==", - "dev": true, - "dependencies": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "node_modules/@types/eslint": { - "version": "8.40.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.40.2.tgz", - "integrity": "sha512-PRVjQ4Eh9z9pmmtaq8nTjZjQwKFk7YIHIud3lRoKRBgUQjgjRmoGxxGEPXQkF+lH7QkHJRNr5F4aBgYCW0lqpQ==", - "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", - "dev": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", - "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", - "dev": true - }, - "node_modules/@types/express": { - "version": "4.17.17", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", - "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", - "dev": true, - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.35", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", - "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/fs-extra": { - "version": "9.0.13", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", - "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", - "dev": true, - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", - "dev": true, - "optional": true, - "dependencies": { - "@types/minimatch": "*", - "@types/node": "*" - } - }, - "node_modules/@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", - "dev": true - }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" - }, - "node_modules/@types/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==", - "dev": true - }, - "node_modules/@types/http-proxy": { - "version": "1.17.11", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.11.tgz", - "integrity": "sha512-HC8G7c1WmaF2ekqpnFq626xd3Zz0uvaqFmBJNRZCGEZCXkvSdJoNFn/8Ygbd9fKNQj8UzLdCETaI0UWPAjK7IA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", - "dev": true - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", - "dev": true - }, - "node_modules/@types/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", - "dev": true, - "optional": true - }, - "node_modules/@types/node": { - "version": "20.4.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.0.tgz", - "integrity": "sha512-jfT7iTf/4kOQ9S7CHV9BIyRaQqHu67mOjsIQBC3BKZvzvUB6zLxEwJ6sBE3ozcvP8kF6Uk5PXN0Q+c0dfhGX0g==" - }, - "node_modules/@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "node_modules/@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "dev": true - }, - "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", - "dev": true - }, - "node_modules/@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", - "dev": true - }, - "node_modules/@types/react": { - "version": "18.2.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.14.tgz", - "integrity": "sha512-A0zjq+QN/O0Kpe30hA1GidzyFjatVvrpIvWLxD+xv67Vt91TWWgco9IvrJBkeyHm1trGaFS/FSGqPlhyeZRm0g==", - "dev": true, - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "18.2.6", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.6.tgz", - "integrity": "sha512-2et4PDvg6PVCyS7fuTc4gPoksV58bW0RwSxWKcPRcHZf0PRUGq03TKcD/rUHe3azfV6/5/biUBJw+HhCQjaP0A==", - "dev": true, - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", - "dev": true - }, - "node_modules/@types/scheduler": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", - "dev": true - }, - "node_modules/@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", - "dev": true - }, - "node_modules/@types/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", - "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", - "dev": true, - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", - "dev": true, - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz", - "integrity": "sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==", - "dev": true, - "dependencies": { - "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" - } - }, - "node_modules/@types/sockjs": { - "version": "0.3.33", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", - "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/triple-beam": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.2.tgz", - "integrity": "sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==" - }, - "node_modules/@types/uuid": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.2.tgz", - "integrity": "sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ==", - "dev": true - }, - "node_modules/@types/ws": { - "version": "8.5.5", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.5.tgz", - "integrity": "sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.0.tgz", - "integrity": "sha512-78B+anHLF1TI8Jn/cD0Q00TBYdMgjdOn980JfAVa9yw5sop8nyTfVOQAv6LWywkOGLclDBtv5z3oxN4w7jxyNg==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.60.0", - "@typescript-eslint/type-utils": "5.60.0", - "@typescript-eslint/utils": "5.60.0", - "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.0.tgz", - "integrity": "sha512-jBONcBsDJ9UoTWrARkRRCgDz6wUggmH5RpQVlt7BimSwaTkTjwypGzKORXbR4/2Hqjk9hgwlon2rVQAjWNpkyQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.60.0", - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/typescript-estree": "5.60.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.0.tgz", - "integrity": "sha512-hakuzcxPwXi2ihf9WQu1BbRj1e/Pd8ZZwVTG9kfbxAMZstKz8/9OoexIwnmLzShtsdap5U/CoQGRCWlSuPbYxQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/visitor-keys": "5.60.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.0.tgz", - "integrity": "sha512-X7NsRQddORMYRFH7FWo6sA9Y/zbJ8s1x1RIAtnlj6YprbToTiQnM6vxcMu7iYhdunmoC0rUWlca13D5DVHkK2g==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "5.60.0", - "@typescript-eslint/utils": "5.60.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.0.tgz", - "integrity": "sha512-ascOuoCpNZBccFVNJRSC6rPq4EmJ2NkuoKnd6LDNyAQmdDnziAtxbCGWCbefG1CNzmDvd05zO36AmB7H8RzKPA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.0.tgz", - "integrity": "sha512-R43thAuwarC99SnvrBmh26tc7F6sPa2B3evkXp/8q954kYL6Ro56AwASYWtEEi+4j09GbiNAHqYwNNZuNlARGQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/visitor-keys": "5.60.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.0.tgz", - "integrity": "sha512-ba51uMqDtfLQ5+xHtwlO84vkdjrqNzOnqrnwbMHMRY8Tqeme8C2Q8Fc7LajfGR+e3/4LoYiWXUM6BpIIbHJ4hQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.60.0", - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/typescript-estree": "5.60.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.0.tgz", - "integrity": "sha512-wm9Uz71SbCyhUKgcaPRauBdTegUyY/ZWl8gLwD/i/ybJqscrrdVSFImpvUz16BLPChIeKBK5Fa9s6KDQjsjyWw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.60.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@vercel/webpack-asset-relocator-loader": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@vercel/webpack-asset-relocator-loader/-/webpack-asset-relocator-loader-1.7.3.tgz", - "integrity": "sha512-vizrI18v8Lcb1PmNNUBz7yxPxxXoOeuaVEjTG9MjvDrphjiSxFZrRJ5tIghk+qdLFRCXI5HBCshgobftbmrC5g==", - "dev": true, - "dependencies": { - "resolve": "^1.10.0" - } - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", - "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", - "dev": true, - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", - "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", - "dev": true, - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", - "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", - "dev": true, - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", - "dev": true, - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", - "dev": true - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", - "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-opt": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6", - "@webassemblyjs/wast-printer": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", - "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", - "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", - "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", - "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webpack-cli/configtest": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", - "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", - "dev": true, - "engines": { - "node": ">=14.15.0" - }, - "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" - } - }, - "node_modules/@webpack-cli/info": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", - "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", - "dev": true, - "engines": { - "node": ">=14.15.0" - }, - "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" - } - }, - "node_modules/@webpack-cli/serve": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", - "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", - "dev": true, - "engines": { - "node": ">=14.15.0" - }, - "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" - }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", - "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "dev": true, - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agentkeepalive": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz", - "integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==", - "dev": true, - "dependencies": { - "debug": "^4.1.0", - "depd": "^2.0.0", - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "dev": true, - "engines": [ - "node >= 0.8.0" - ], - "bin": { - "ansi-html": "bin/ansi-html" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "dev": true - }, - "node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "dev": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true - }, - "node_modules/array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/asar": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/asar/-/asar-3.2.0.tgz", - "integrity": "sha512-COdw2ZQvKdFGFxXwX3oYh2/sOsJWJegrdJCGxnN4MZ7IULgRBp9P6665aqj9z1v9VwP4oP1hRBojRDQ//IGgAg==", - "deprecated": "Please use @electron/asar moving forward. There is no API change, just a package name change", - "dev": true, - "optional": true, - "dependencies": { - "chromium-pickle-js": "^0.2.0", - "commander": "^5.0.0", - "glob": "^7.1.6", - "minimatch": "^3.0.4" - }, - "bin": { - "asar": "bin/asar.js" - }, - "engines": { - "node": ">=10.12.0" - }, - "optionalDependencies": { - "@types/glob": "^7.1.1" - } - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/atomically": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", - "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==", - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/author-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/author-regex/-/author-regex-1.0.0.tgz", - "integrity": "sha512-KbWgR8wOYRAPekEmMXrYYdc7BRyhn2Ftk7KWfMUnQ43hFdojWEFRxhhRUm3/OFEdPa1r0KAvTTg9YQK57xTe0g==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/autoprefixer": { - "version": "10.4.14", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", - "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - } - ], - "dependencies": { - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001464", - "fraction.js": "^4.2.0", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/babel-loader": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.2.tgz", - "integrity": "sha512-mN14niXW43tddohGl8HPu5yfQq70iUThvFL/4QzESA7GcZoC0eVOhvWdQ8+3UlSjaDE9MVtsW9mxDY07W7VpVA==", - "dev": true, - "dependencies": { - "find-cache-dir": "^3.3.2", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0", - "webpack": ">=5" - } - }, - "node_modules/babel-loader/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/babel-loader/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/babel-loader/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/babel-loader/node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.4.tgz", - "integrity": "sha512-9WeK9snM1BfxB38goUEv2FLnA6ja07UMfazFHzCXUb3NyDZAwfXvQiURQ6guTTMeHcOsdknULm1PDhs4uWtKyA==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.4.1", - "@nicolo-ribaudo/semver-v6": "^6.3.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.2.tgz", - "integrity": "sha512-Cid+Jv1BrY9ReW9lIfNlNpsI53N+FN7gE+f73zLAUbr9C52W4gKLWSByx47pfDJsEysojKArqOtOKZSVIIUTuQ==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.1", - "core-js-compat": "^3.31.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.1.tgz", - "integrity": "sha512-L8OyySuI6OSQ5hFy9O+7zFjyr4WhAfRjLIOkhQGYl+emwJkd/S4XXT1JpfrgR1jrQ1NcGiOh+yAdGlF8pnC3Jw==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", - "dev": true - }, - "node_modules/before-after-hook": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", - "dev": true - }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dev": true, - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/bonjour-service": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", - "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", - "dev": true, - "dependencies": { - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" - } - }, - "node_modules/bonjour-service/node_modules/array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true - }, - "node_modules/boolean": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", - "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", - "optional": true - }, - "node_modules/bottleneck": { - "version": "2.19.5", - "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", - "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.21.9", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", - "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001503", - "electron-to-chromium": "^1.4.431", - "node-releases": "^2.0.12", - "update-browserslist-db": "^1.0.11" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "engines": { - "node": "*" - } - }, - "node_modules/buffer-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", - "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", - "dev": true, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cacache": { - "version": "17.1.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.1.3.tgz", - "integrity": "sha512-jAdjGxmPxZh0IipMdR7fK/4sDSrHMLUV0+GvVUsjwyGNKHsh79kW/otg+GkbXwl6Uzvy9wsvHOX4nUoWldeZMg==", - "dev": true, - "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/cacache/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/cacache/node_modules/glob": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.0.tgz", - "integrity": "sha512-AQ1/SB9HH0yCx1jXAT4vmCbTOPe5RQ+kCurjbel5xSCGhebumUv+GJZfa1rEqor3XIViqwSEmlkZCQD43RWrBg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2", - "path-scurry": "^1.7.0" - }, - "bin": { - "glob": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/cacache/node_modules/minimatch": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", - "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "engines": { - "node": ">=10.6.0" - } - }, - "node_modules/cacheable-request": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", - "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dev": true, - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001507", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001507.tgz", - "integrity": "sha512-SFpUDoSLCaE5XYL2jfqe9ova/pbQHEmbheDf5r4diNwbAgR3qxM9NQtfsiSscjqoya5K7kFcHPUQ+VsUkIJR4A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chmodr": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/chmodr/-/chmodr-1.2.0.tgz", - "integrity": "sha512-Y5uI7Iq/Az6HgJEL6pdw7THVd7jbVOTPwsmcPOBjQL8e3N+pz872kzK5QxYGEy21iRys+iHWV0UZQXDFJo1hyA==", - "dev": true - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/chromium-pickle-js": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", - "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", - "dev": true - }, - "node_modules/clean-css": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", - "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==", - "dev": true, - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 10.0" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz", - "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true, - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/color/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true - }, - "node_modules/colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", - "dependencies": { - "color": "^3.1.3", - "text-hex": "1.0.x" - } - }, - "node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, - "node_modules/compare-version": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", - "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dev": true, - "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/compression/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/conf": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/conf/-/conf-10.2.0.tgz", - "integrity": "sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==", - "dependencies": { - "ajv": "^8.6.3", - "ajv-formats": "^2.1.1", - "atomically": "^1.7.0", - "debounce-fn": "^4.0.0", - "dot-prop": "^6.0.1", - "env-paths": "^2.2.1", - "json-schema-typed": "^7.0.3", - "onetime": "^5.1.2", - "pkg-up": "^3.1.0", - "semver": "^7.3.5" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/conf/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/conf/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "dev": true - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "dev": true - }, - "node_modules/copy-to-clipboard": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", - "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", - "dependencies": { - "toggle-selection": "^1.0.6" - } - }, - "node_modules/copy-webpack-plugin": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", - "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", - "dev": true, - "dependencies": { - "fast-glob": "^3.2.11", - "glob-parent": "^6.0.1", - "globby": "^13.1.1", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/copy-webpack-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/copy-webpack-plugin/node_modules/globby": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.0.tgz", - "integrity": "sha512-jWsQfayf13NvqKUIL3Ta+CIqMnvlaIDFveWE/dpOZ9+3AMEJozsxDvKA02zync9UuvOM8rOXzsD5GqKP4OnWPQ==", - "dev": true, - "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.11", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/copy-webpack-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/copy-webpack-plugin/node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/copy-webpack-plugin/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/core-js-compat": { - "version": "3.31.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.31.1.tgz", - "integrity": "sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA==", - "dev": true, - "dependencies": { - "browserslist": "^4.21.9" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true - }, - "node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "dev": true, - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cross-spawn-windows-exe": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/cross-spawn-windows-exe/-/cross-spawn-windows-exe-1.2.0.tgz", - "integrity": "sha512-mkLtJJcYbDCxEG7Js6eUnUNndWjyUZwJ3H7bErmmtOYU/Zb99DyUkpamuIZE0b3bhmJyZ7D90uS6f+CGxRRjOw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-cross-spawn-windows-exe?utm_medium=referral&utm_source=npm_fund" - } - ], - "dependencies": { - "@malept/cross-spawn-promise": "^1.1.0", - "is-wsl": "^2.2.0", - "which": "^2.0.2" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cross-spawn-windows-exe/node_modules/@malept/cross-spawn-promise": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", - "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" - } - ], - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cross-zip": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cross-zip/-/cross-zip-4.0.0.tgz", - "integrity": "sha512-MEzGfZo0rqE10O/B+AEcCSJLZsrWuRUvmqJTqHNqBtALhaJc3E3ixLGLJNTRzEA2K34wbmOHC4fwYs9sVsdcCA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "engines": { - "node": ">=12.10" - } - }, - "node_modules/css-blank-pseudo": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-5.0.2.tgz", - "integrity": "sha512-aCU4AZ7uEcVSUzagTlA9pHciz7aWPKA/YzrEkpdSopJ2pvhIxiQ5sYeMz1/KByxlIo4XBdvMNJAVKMg/GRnhfw==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-has-pseudo": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-5.0.2.tgz", - "integrity": "sha512-q+U+4QdwwB7T9VEW/LyO6CFrLAeLqOykC5mDqJXc7aKZAhDbq7BvGT13VGJe+IwBfdN2o3Xdw2kJ5IxwV1Sc9Q==", - "dev": true, - "dependencies": { - "@csstools/selector-specificity": "^2.0.1", - "postcss-selector-parser": "^6.0.10", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-loader": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.8.1.tgz", - "integrity": "sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g==", - "dev": true, - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.21", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.3", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.3.8" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/css-prefers-color-scheme": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-8.0.2.tgz", - "integrity": "sha512-OvFghizHJ45x7nsJJUSYLyQNTzsCU8yWjxAc/nhPQg1pbs18LMoET8N3kOweFDPy0JV0OSXN2iqRFhPBHYOeMA==", - "dev": true, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "dev": true, - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "dev": true, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/cssdb": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.6.0.tgz", - "integrity": "sha512-Nna7rph8V0jC6+JBY4Vk4ndErUmfJfV6NJCaZdurL0omggabiy+QB2HCQtu5c/ACLZ0I7REv7A4QyPIoYzZx0w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - } - ] - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/csso": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", - "dev": true, - "dependencies": { - "css-tree": "~2.2.0" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/css-tree": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", - "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", - "dev": true, - "dependencies": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.28", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "dev": true - }, - "node_modules/csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", - "dev": true - }, - "node_modules/debounce-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", - "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", - "dependencies": { - "mimic-fn": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/debounce-fn/node_modules/mimic-fn": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", - "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", - "dev": true, - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "engines": { - "node": ">=10" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", - "devOptional": true, - "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", - "dev": true - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "devOptional": true - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dir-compare": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz", - "integrity": "sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==", - "dev": true, - "dependencies": { - "buffer-equal": "^1.0.0", - "minimatch": "^3.0.4" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true - }, - "node_modules/dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", - "dev": true - }, - "node_modules/dns-packet": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.0.tgz", - "integrity": "sha512-rza3UH1LwdHh9qyPXp8lkwpjSNk/AMD3dPytUoRoqnypDUhY0xvbdmVhWOfxO68frEfV9BU8V12Ez7ZsHGZpCQ==", - "dev": true, - "dependencies": { - "@leichtgewicht/ip-codec": "^2.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "dev": true, - "dependencies": { - "utila": "~0.4" - } - }, - "node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dev": true, - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dev": true, - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/dot-prop": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dset": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.2.tgz", - "integrity": "sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q==", - "engines": { - "node": ">=4" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true - }, - "node_modules/electron": { - "version": "25.9.2", - "resolved": "https://registry.npmjs.org/electron/-/electron-25.9.2.tgz", - "integrity": "sha512-hVBN5rsrL99BKNHvzMeYy2PkAmewuIobu4U3o3EzVz4MDoLmMfW4yTH5GZ4RbJrpokoEky5IzGtRR/ggPzL6Fw==", - "hasInstallScript": true, - "dependencies": { - "@electron/get": "^2.0.0", - "@types/node": "^18.11.18", - "extract-zip": "^2.0.1" - }, - "bin": { - "electron": "cli.js" - }, - "engines": { - "node": ">= 12.20.55" - } - }, - "node_modules/electron-installer-common": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/electron-installer-common/-/electron-installer-common-0.10.3.tgz", - "integrity": "sha512-mYbP+6i+nHMIm0WZHXgGdmmXMe+KXncl6jZYQNcCF9C1WsNA9C5SZ2VP4TLQMSIoFO+X4ugkMEA5uld1bmyEvA==", - "dev": true, - "optional": true, - "dependencies": { - "@malept/cross-spawn-promise": "^1.0.0", - "asar": "^3.0.0", - "debug": "^4.1.1", - "fs-extra": "^9.0.0", - "glob": "^7.1.4", - "lodash": "^4.17.15", - "parse-author": "^2.0.0", - "semver": "^7.1.1", - "tmp-promise": "^3.0.2" - }, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "url": "https://github.com/electron-userland/electron-installer-common?sponsor=1" - }, - "optionalDependencies": { - "@types/fs-extra": "^9.0.1" - } - }, - "node_modules/electron-installer-common/node_modules/@malept/cross-spawn-promise": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", - "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" - } - ], - "optional": true, - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/electron-installer-common/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "optional": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/electron-installer-debian": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/electron-installer-debian/-/electron-installer-debian-3.2.0.tgz", - "integrity": "sha512-58ZrlJ1HQY80VucsEIG9tQ//HrTlG6sfofA3nRGr6TmkX661uJyu4cMPPh6kXW+aHdq/7+q25KyQhDrXvRL7jw==", - "dev": true, - "optional": true, - "os": [ - "darwin", - "linux" - ], - "dependencies": { - "@malept/cross-spawn-promise": "^1.0.0", - "debug": "^4.1.1", - "electron-installer-common": "^0.10.2", - "fs-extra": "^9.0.0", - "get-folder-size": "^2.0.1", - "lodash": "^4.17.4", - "word-wrap": "^1.2.3", - "yargs": "^16.0.2" - }, - "bin": { - "electron-installer-debian": "src/cli.js" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/electron-installer-debian/node_modules/@malept/cross-spawn-promise": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", - "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" - } - ], - "optional": true, - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/electron-installer-debian/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "optional": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/electron-installer-debian/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "optional": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/electron-installer-debian/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "optional": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/electron-installer-debian/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "optional": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/electron-installer-redhat": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/electron-installer-redhat/-/electron-installer-redhat-3.4.0.tgz", - "integrity": "sha512-gEISr3U32Sgtj+fjxUAlSDo3wyGGq6OBx7rF5UdpIgbnpUvMN4W5uYb0ThpnAZ42VEJh/3aODQXHbFS4f5J3Iw==", - "dev": true, - "optional": true, - "os": [ - "darwin", - "linux" - ], - "dependencies": { - "@malept/cross-spawn-promise": "^1.0.0", - "debug": "^4.1.1", - "electron-installer-common": "^0.10.2", - "fs-extra": "^9.0.0", - "lodash": "^4.17.15", - "word-wrap": "^1.2.3", - "yargs": "^16.0.2" - }, - "bin": { - "electron-installer-redhat": "src/cli.js" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/electron-installer-redhat/node_modules/@malept/cross-spawn-promise": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", - "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" - } - ], - "optional": true, - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/electron-installer-redhat/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "optional": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/electron-installer-redhat/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "optional": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/electron-installer-redhat/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "optional": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/electron-installer-redhat/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "optional": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/electron-packager": { - "version": "17.1.2", - "resolved": "https://registry.npmjs.org/electron-packager/-/electron-packager-17.1.2.tgz", - "integrity": "sha512-XofXdikjYI7MVBcnXeoOvRR+yFFFHOLs3J7PF5KYQweigtgLshcH4W660PsvHr4lYZ03JBpLyEcUB8DzHZ+BNw==", - "dev": true, - "dependencies": { - "@electron/asar": "^3.2.1", - "@electron/get": "^2.0.0", - "@electron/notarize": "^1.2.3", - "@electron/osx-sign": "^1.0.5", - "@electron/universal": "^1.3.2", - "cross-spawn-windows-exe": "^1.2.0", - "debug": "^4.0.1", - "extract-zip": "^2.0.0", - "filenamify": "^4.1.0", - "fs-extra": "^11.1.0", - "galactus": "^1.0.0", - "get-package-info": "^1.0.0", - "junk": "^3.1.0", - "parse-author": "^2.0.0", - "plist": "^3.0.0", - "rcedit": "^3.0.1", - "resolve": "^1.1.6", - "semver": "^7.1.3", - "yargs-parser": "^21.1.1" - }, - "bin": { - "electron-packager": "bin/electron-packager.js" - }, - "engines": { - "node": ">= 14.17.5" - }, - "funding": { - "url": "https://github.com/electron/electron-packager?sponsor=1" - } - }, - "node_modules/electron-packager/node_modules/fs-extra": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", - "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/electron-squirrel-startup": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/electron-squirrel-startup/-/electron-squirrel-startup-1.0.0.tgz", - "integrity": "sha512-Oce8mvgGdFmwr+DsAcXBmFK8jFfN6yaFAP9IvyhTfupM3nFkBku/7VS/mdtJteWumImkC6P+BKGsxScoDDkv9Q==", - "dependencies": { - "debug": "^2.2.0" - } - }, - "node_modules/electron-squirrel-startup/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/electron-squirrel-startup/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/electron-store": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-8.1.0.tgz", - "integrity": "sha512-2clHg/juMjOH0GT9cQ6qtmIvK183B39ZXR0bUoPwKwYHJsEF3quqyDzMFUAu+0OP8ijmN2CbPRAelhNbWUbzwA==", - "dependencies": { - "conf": "^10.2.0", - "type-fest": "^2.17.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/electron-store/node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.440", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.440.tgz", - "integrity": "sha512-r6dCgNpRhPwiWlxbHzZQ/d9swfPaEJGi8ekqRBwQYaR3WmA5VkqQfBWSDDjuJU1ntO+W9tHx8OHV/96Q8e0dVw==", - "dev": true - }, - "node_modules/electron-winstaller": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.1.0.tgz", - "integrity": "sha512-4wlZzkUm5cJNiOtp5wL804+QpygdKTKkrZJXA3sSDEI2XnCVPv0kxmxUvVw4KHBwbNS+Yox89agEr+VkR7kxww==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "dependencies": { - "@electron/asar": "^3.2.1", - "debug": "^4.1.1", - "fs-extra": "^7.0.1", - "lodash.template": "^4.2.2", - "temp": "^0.9.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/electron-winstaller/node_modules/fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, - "optional": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/electron-winstaller/node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "optional": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/electron-winstaller/node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "optional": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/electron/node_modules/@types/node": { - "version": "18.16.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.18.tgz", - "integrity": "sha512-/aNaQZD0+iSBAGnvvN2Cx92HqE5sZCPZtx2TsK+4nvV23fFe09jVDvpArXr2j9DnYlzuU9WuoykDDc6wqvpNcw==" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", - "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/envinfo": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.9.0.tgz", - "integrity": "sha512-RODB4txU+xImYDemN5DqaKC0CHk05XSVkOX4pq0hK26Qx+1LChkuOyUDlGEjYb3ACr0n9qBhFjg37hQuJvpkRQ==", - "dev": true, - "bin": { - "envinfo": "dist/cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "dev": true - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.21.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", - "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.0", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-module-lexer": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", - "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==", - "dev": true - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "optional": true - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "dev": true - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "devOptional": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", - "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.43.0", - "@humanwhocodes/config-array": "^0.11.10", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", - "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.11.0", - "resolve": "^1.22.1" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", - "dev": true, - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.27.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", - "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.7.4", - "has": "^1.0.3", - "is-core-module": "^2.11.0", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.values": "^1.1.6", - "resolve": "^1.22.1", - "semver": "^6.3.0", - "tsconfig-paths": "^3.14.1" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/espree": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", - "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", - "dev": true, - "dependencies": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/execa/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", - "dev": true, - "dependencies": { - "homedir-polyfill": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/exponential-backoff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", - "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", - "dev": true - }, - "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "dev": true, - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express-ws": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/express-ws/-/express-ws-5.0.2.tgz", - "integrity": "sha512-0uvmuk61O9HXgLhGl3QhNSEtRsQevtmbL94/eILaliEADZBHZOQUAiHFrGPrgsjikohyrmSG5g+sCfASTt0lkQ==", - "dev": true, - "dependencies": { - "ws": "^7.4.6" - }, - "engines": { - "node": ">=4.5.0" - }, - "peerDependencies": { - "express": "^4.0.0 || ^5.0.0-alpha.1" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", - "dev": true, - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "dev": true, - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "dependencies": { - "pend": "~1.2.0" - } - }, - "node_modules/fecha": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/file-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", - "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/file-stream-rotator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz", - "integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==", - "dependencies": { - "moment": "^2.29.1" - } - }, - "node_modules/filename-reserved-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", - "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/filenamify": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", - "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", - "dev": true, - "dependencies": { - "filename-reserved-regex": "^2.0.0", - "strip-outer": "^1.0.1", - "trim-repeated": "^1.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "node_modules/flora-colossus": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/flora-colossus/-/flora-colossus-2.0.0.tgz", - "integrity": "sha512-dz4HxH6pOvbUzZpZ/yXhafjbR2I8cenK5xL0KtBFb7U2ADsR+OwXifnxZjij/pZWF775uSCMzWVd+jDik2H2IA==", - "dev": true, - "dependencies": { - "debug": "^4.3.4", - "fs-extra": "^10.1.0" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", - "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fork-ts-checker-webpack-plugin": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.3.0.tgz", - "integrity": "sha512-IN+XTzusCjR5VgntYFgxbxVx3WraPRnKehBFrf00cMSrtUuW9MsG9dhL6MWpY6MkjC3wVwoujfCDgZZCQwbswA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.7", - "chalk": "^4.1.2", - "chokidar": "^3.5.3", - "cosmiconfig": "^7.0.1", - "deepmerge": "^4.2.2", - "fs-extra": "^10.0.0", - "memfs": "^3.4.1", - "minimatch": "^3.0.4", - "node-abort-controller": "^3.0.1", - "schema-utils": "^3.1.1", - "semver": "^7.3.5", - "tapable": "^2.2.1" - }, - "engines": { - "node": ">=12.13.0", - "yarn": ">=1.0.0" - }, - "peerDependencies": { - "typescript": ">3.6.0", - "vue-template-compiler": "*", - "webpack": "^5.11.0" - }, - "peerDependenciesMeta": { - "vue-template-compiler": { - "optional": true - } - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", - "dev": true, - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://www.patreon.com/infusion" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/fs-minipass": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.2.tgz", - "integrity": "sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g==", - "dev": true, - "dependencies": { - "minipass": "^5.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/fs-monkey": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.4.tgz", - "integrity": "sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ==", - "dev": true - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "devOptional": true - }, - "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/galactus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/galactus/-/galactus-1.0.0.tgz", - "integrity": "sha512-R1fam6D4CyKQGNlvJne4dkNF+PvUUl7TAJInvTGa9fti9qAv95quQz29GXapA4d8Ec266mJJxFVh82M4GIIGDQ==", - "dev": true, - "dependencies": { - "debug": "^4.3.4", - "flora-colossus": "^2.0.0", - "fs-extra": "^10.1.0" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/gar": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/gar/-/gar-1.0.4.tgz", - "integrity": "sha512-w4n9cPWyP7aHxKxYHFQMegj7WIAsL/YX/C4Bs5Rr8s1H9M1rNtRWRsw+ovYMkXDQ5S4ZbYHsHAPmevPjPgw44w==", - "dev": true, - "optional": true - }, - "node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "dev": true, - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-folder-size": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/get-folder-size/-/get-folder-size-2.0.1.tgz", - "integrity": "sha512-+CEb+GDCM7tkOS2wdMKTn9vU7DgnKUTuDlehkNJKNSovdCOVxs14OfKCk4cvSaR3za4gj+OBdl9opPN9xrJ0zA==", - "dev": true, - "optional": true, - "dependencies": { - "gar": "^1.0.4", - "tiny-each-async": "2.0.3" - }, - "bin": { - "get-folder-size": "bin/get-folder-size" - } - }, - "node_modules/get-installed-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/get-installed-path/-/get-installed-path-2.1.1.tgz", - "integrity": "sha512-Qkn9eq6tW5/q9BDVdMpB8tOHljX9OSP0jRC5TRNVA4qRc839t4g8KQaR8t0Uv0EFVL0MlyG7m/ofjEgAROtYsA==", - "dev": true, - "dependencies": { - "global-modules": "1.0.0" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "devOptional": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-package-info": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-package-info/-/get-package-info-1.0.0.tgz", - "integrity": "sha512-SCbprXGAPdIhKAXiG+Mk6yeoFH61JlYunqdFQFHDtLjJlDjFf6x07dsS8acO+xWt52jpdVo49AlVDnUVK1sDNw==", - "dev": true, - "dependencies": { - "bluebird": "^3.1.1", - "debug": "^2.2.0", - "lodash.get": "^4.0.0", - "read-pkg-up": "^2.0.0" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/get-package-info/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/get-package-info/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, - "node_modules/global-agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", - "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", - "optional": true, - "dependencies": { - "boolean": "^3.0.1", - "es6-error": "^4.1.1", - "matcher": "^3.0.0", - "roarr": "^2.15.3", - "semver": "^7.3.2", - "serialize-error": "^7.0.1" - }, - "engines": { - "node": ">=10.0" - } - }, - "node_modules/global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "dependencies": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", - "dev": true, - "dependencies": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "devOptional": true, - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/got": { - "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", - "dependencies": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=10.19.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "dev": true - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "devOptional": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "devOptional": true, - "dependencies": { - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "devOptional": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "devOptional": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, - "dependencies": { - "parse-passwd": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "node_modules/hpack.js/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/hpack.js/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/hpack.js/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/html-entities": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.6.tgz", - "integrity": "sha512-9o0+dcpIw2/HxkNuYKxSJUF/MMRZQECK4GnF+oQOmJ83yCVHTWgCH5aOXxK5bozNRmM8wtgryjHD3uloPBDEGw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/mdevils" - }, - { - "type": "patreon", - "url": "https://patreon.com/mdevils" - } - ] - }, - "node_modules/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", - "dev": true, - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" - }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/html-minifier-terser/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "dev": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/html-webpack-plugin": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.3.tgz", - "integrity": "sha512-6YrDKTuqaP/TquFH7h4srYWsZx+x6k6+FbsTm0ziCwGHDP78Unr1r9F/H4+sGmMbX08GQcJ+K64x55b+7VM/jg==", - "dev": true, - "dependencies": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/html-webpack-plugin" - }, - "peerDependencies": { - "webpack": "^5.20.0" - } - }, - "node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" - }, - "node_modules/http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", - "dev": true - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", - "dev": true - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dev": true, - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", - "dev": true, - "dependencies": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } - } - }, - "node_modules/http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "dev": true, - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/interpret": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", - "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", - "dev": true, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", - "dev": true - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "dev": true - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/isbinaryfile": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", - "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", - "dev": true, - "engines": { - "node": ">= 8.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/gjtorikian/" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jackspeak": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz", - "integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jiti": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", - "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==", - "dev": true, - "bin": { - "jiti": "bin/jiti.js" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-schema-typed": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz", - "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "optional": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/junk": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", - "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/keyv": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", - "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "node_modules/launch-editor": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.0.tgz", - "integrity": "sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==", - "dev": true, - "dependencies": { - "picocolors": "^1.0.0", - "shell-quote": "^1.7.3" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/listr2": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.8.tgz", - "integrity": "sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA==", - "dev": true, - "dependencies": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.19", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rfdc": "^1.3.0", - "rxjs": "^7.8.0", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": "^14.13.1 || >=16.0.0" - }, - "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" - }, - "peerDependenciesMeta": { - "enquirer": { - "optional": true - } - } - }, - "node_modules/load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", - "dev": true, - "dependencies": { - "error-ex": "^1.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true, - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==", - "dev": true, - "optional": true - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true - }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lodash.template": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", - "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", - "dev": true, - "optional": true, - "dependencies": { - "lodash._reinterpolate": "^3.0.0", - "lodash.templatesettings": "^4.0.0" - } - }, - "node_modules/lodash.templatesettings": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", - "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", - "dev": true, - "optional": true, - "dependencies": { - "lodash._reinterpolate": "^3.0.0" - } - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/logform": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", - "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==", - "dependencies": { - "@colors/colors": "1.5.0", - "@types/triple-beam": "^1.3.2", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/make-fetch-happen": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", - "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==", - "dev": true, - "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^17.0.0", - "http-cache-semantics": "^4.1.1", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^10.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "dependencies": { - "p-defer": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/matcher": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", - "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", - "optional": true, - "dependencies": { - "escape-string-regexp": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "dependencies": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", - "dev": true, - "dependencies": { - "fs-monkey": "^1.0.4" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", - "dev": true - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-collect/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-fetch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.3.tgz", - "integrity": "sha512-n5ITsTkDqYkYJZjcRWzZt9qnZKCT7nKCosJhHoj7S7zD+BP4jVbWs+odsniw5TA3E0sLomhTKOKjF86wf11PuQ==", - "dev": true, - "dependencies": { - "minipass": "^5.0.0", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-flush/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-pipeline/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", - "dev": true, - "dependencies": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" - }, - "bin": { - "multicast-dns": "cli.js" - } - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-abi": { - "version": "3.45.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.45.0.tgz", - "integrity": "sha512-iwXuFrMAcFVi/ZoZiqq8BzAdsLw9kxDfTC0HMyjXfSL/6CSDAGD5UmR7azrAgWV1zKYq7dUUMj4owusBWKLsiQ==", - "dev": true, - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", - "dev": true - }, - "node_modules/node-api-version": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.1.4.tgz", - "integrity": "sha512-KGXihXdUChwJAOHO53bv9/vXcLmdUsZ6jIptbvYvkpKfth+r7jw44JkVxQFA3kX5nQjzjmGu1uAu/xNNLNlI5g==", - "dev": true, - "dependencies": { - "semver": "^7.3.5" - } - }, - "node_modules/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "dev": true, - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/node-gyp": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.0.tgz", - "integrity": "sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg==", - "dev": true, - "dependencies": { - "env-paths": "^2.2.0", - "exponential-backoff": "^3.1.1", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^11.0.3", - "nopt": "^6.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": "^12.13 || ^14.13 || >=16" - } - }, - "node_modules/node-loader": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/node-loader/-/node-loader-2.0.0.tgz", - "integrity": "sha512-I5VN34NO4/5UYJaUBtkrODPWxbobrE4hgDqPrjB25yPkonFhCmZ146vTH+Zg417E9Iwoh1l/MbRs1apc5J295Q==", - "dev": true, - "dependencies": { - "loader-utils": "^2.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/node-releases": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", - "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==", - "dev": true - }, - "node_modules/nopt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", - "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", - "dev": true, - "dependencies": { - "abbrev": "^1.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "dev": true, - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "devOptional": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "dependencies": { - "fn.name": "1.x.x" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "dev": true, - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "dev": true, - "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-retry/node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dev": true, - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-author": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/parse-author/-/parse-author-2.0.0.tgz", - "integrity": "sha512-yx5DfvkN8JsHL2xk2Os9oTia467qnvRgey4ahSm2X8epehBLx/gWLcy5KI+Y36ful5DzGbCS6RazqZGgy1gHNw==", - "dev": true, - "dependencies": { - "author-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dev": true, - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-scurry": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.9.2.tgz", - "integrity": "sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==", - "dev": true, - "dependencies": { - "lru-cache": "^9.1.1", - "minipass": "^5.0.0 || ^6.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.2.tgz", - "integrity": "sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-up/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-up/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/plist": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/plist/-/plist-3.0.6.tgz", - "integrity": "sha512-WiIVYyrp8TD4w8yCvyeIr+lkmrGRd5u0VbRnU+tP/aRLxP/YadJUYOMZJ/6hIa3oUyVCsycXvtNRgd5XBJIbiA==", - "dev": true, - "dependencies": { - "base64-js": "^1.5.1", - "xmlbuilder": "^15.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-attribute-case-insensitive": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-6.0.2.tgz", - "integrity": "sha512-IRuCwwAAQbgaLhxQdQcIIK0dCVXg3XDUnzgKD8iwdiYdwU4rMWRWyl/W9/0nA4ihVpq5pyALiHB2veBJ0292pw==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-clamp": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", - "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=7.6.0" - }, - "peerDependencies": { - "postcss": "^8.4.6" - } - }, - "node_modules/postcss-color-functional-notation": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-5.1.0.tgz", - "integrity": "sha512-w2R4py6zrVE1U7FwNaAc76tNQlG9GLkrBbcFw+VhUjyDDiV28vfZG+l4LyPmpoQpeSJVtu8VgNjE8Jv5SpC7dQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^2.3.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-color-hex-alpha": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-9.0.2.tgz", - "integrity": "sha512-SfPjgr//VQ/DOCf80STIAsdAs7sbIbxATvVmd+Ec7JvR8onz9pjawhq3BJM3Pie40EE3TyB0P6hft16D33Nlyg==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-color-rebeccapurple": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-8.0.2.tgz", - "integrity": "sha512-xWf/JmAxVoB5bltHpXk+uGRoGFwu4WDAR7210el+iyvTdqiKpDhtcT8N3edXMoVJY0WHFMrKMUieql/wRNiXkw==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-custom-media": { - "version": "9.1.5", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-9.1.5.tgz", - "integrity": "sha512-GStyWMz7Qbo/Gtw1xVspzVSX8eipgNg4lpsO3CAeY4/A1mzok+RV6MCv3fg62trWijh/lYEj6vps4o8JcBBpDA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/cascade-layer-name-parser": "^1.0.2", - "@csstools/css-parser-algorithms": "^2.2.0", - "@csstools/css-tokenizer": "^2.1.1", - "@csstools/media-query-list-parser": "^2.1.1" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-custom-properties": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-13.2.0.tgz", - "integrity": "sha512-UYiPqbqmVayyv56y0mtGhvUKZClflwE9cTTmPaqEX8fOVjVwsotqKGYtJXSLxrJLwf9tt7ka+Luyh1ZAOhGHWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/cascade-layer-name-parser": "^1.0.2", - "@csstools/css-parser-algorithms": "^2.1.1", - "@csstools/css-tokenizer": "^2.1.1", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-custom-selectors": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-7.1.3.tgz", - "integrity": "sha512-GTVscax6O/8s7agFF0HsOoIyjrnAbLjgCUle8tn+0oDGJuVx7p56U7ClSRoC49poxFuMfu2B4Q8GnxSCOeuFKw==", - "dev": true, - "dependencies": { - "@csstools/cascade-layer-name-parser": "^1.0.2", - "@csstools/css-parser-algorithms": "^2.1.1", - "@csstools/css-tokenizer": "^2.1.1", - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-dir-pseudo-class": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-7.0.2.tgz", - "integrity": "sha512-cMnslilYxBf9k3qejnovrUONZx1rXeUZJw06fgIUBzABJe3D2LiLL5WAER7Imt3nrkaIgG05XZBztueLEf5P8w==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-double-position-gradients": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-4.0.4.tgz", - "integrity": "sha512-nUAbUXURemLXIrl4Xoia2tiu5z/n8sY+BVDZApoeT9BlpByyrp02P/lFCRrRvZ/zrGRE+MOGLhk8o7VcMCtPtQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^2.3.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-focus-visible": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-8.0.2.tgz", - "integrity": "sha512-f/Vd+EC/GaKElknU59esVcRYr/Y3t1ZAQyL4u2xSOgkDy4bMCmG7VP5cGvj3+BTLNE9ETfEuz2nnt4qkZwTTeA==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-focus-within": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-7.0.2.tgz", - "integrity": "sha512-AHAJ89UQBcqBvFgQJE9XasGuwMNkKsGj4D/f9Uk60jFmEBHpAL14DrnSk3Rj+SwZTr/WUG+mh+Rvf8fid/346w==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-font-variant": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", - "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "dev": true, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-gap-properties": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-4.0.1.tgz", - "integrity": "sha512-V5OuQGw4lBumPlwHWk/PRfMKjaq/LTGR4WDTemIMCaMevArVfCCA9wBJiL1VjDAd+rzuCIlkRoRvDsSiAaZ4Fg==", - "dev": true, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-image-set-function": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-5.0.2.tgz", - "integrity": "sha512-Sszjwo0ubETX0Fi5MvpYzsONwrsjeabjMoc5YqHvURFItXgIu3HdCjcVuVKGMPGzKRhgaknmdM5uVWInWPJmeg==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-initial": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", - "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", - "dev": true, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "dev": true, - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-lab-function": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-5.2.3.tgz", - "integrity": "sha512-fi32AYKzji5/rvgxo5zXHFvAYBw0u0OzELbeCNjEZVLUir18Oj+9RmNphtM8QdLUaUnrfx8zy8vVYLmFLkdmrQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/css-color-parser": "^1.2.0", - "@csstools/css-parser-algorithms": "^2.1.1", - "@csstools/css-tokenizer": "^2.1.1", - "@csstools/postcss-progressive-custom-properties": "^2.3.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-load-config": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", - "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", - "dev": true, - "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^2.1.1" - }, - "engines": { - "node": ">= 14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-load-config/node_modules/yaml": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", - "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", - "dev": true, - "engines": { - "node": ">= 14" - } - }, - "node_modules/postcss-loader": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.3.tgz", - "integrity": "sha512-YgO/yhtevGO/vJePCQmTxiaEwER94LABZN0ZMT4A0vsak9TpO+RvKRs7EmJ8peIlB9xfXCsS7M8LjqncsUZ5HA==", - "dev": true, - "dependencies": { - "cosmiconfig": "^8.2.0", - "jiti": "^1.18.2", - "semver": "^7.3.8" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "postcss": "^7.0.0 || ^8.0.1", - "webpack": "^5.0.0" - } - }, - "node_modules/postcss-loader/node_modules/cosmiconfig": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", - "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", - "dev": true, - "dependencies": { - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - } - }, - "node_modules/postcss-logical": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-6.2.0.tgz", - "integrity": "sha512-aqlfKGaY0nnbgI9jwUikp4gJKBqcH5noU/EdnIVceghaaDPYhZuyJVxlvWNy55tlTG5tunRKCTAX9yljLiFgmw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", - "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", - "dev": true, - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dev": true, - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-nested": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", - "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.11" - }, - "engines": { - "node": ">=12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-nesting": { - "version": "11.3.0", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-11.3.0.tgz", - "integrity": "sha512-JlS10AQm/RzyrUGgl5irVkAlZYTJ99mNueUl+Qab+TcHhVedLiylWVkKBhRale+rS9yWIJK48JVzQlq3LcSdeA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/selector-specificity": "^2.0.0", - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-opacity-percentage": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-2.0.0.tgz", - "integrity": "sha512-lyDrCOtntq5Y1JZpBFzIWm2wG9kbEdujpNt4NLannF+J9c8CgFIzPa80YQfdza+Y+yFfzbYj/rfoOsYsooUWTQ==", - "dev": true, - "funding": [ - { - "type": "kofi", - "url": "https://ko-fi.com/mrcgrtz" - }, - { - "type": "liberapay", - "url": "https://liberapay.com/mrcgrtz" - } - ], - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-overflow-shorthand": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-4.0.1.tgz", - "integrity": "sha512-HQZ0qi/9iSYHW4w3ogNqVNr2J49DHJAl7r8O2p0Meip38jsdnRPgiDW7r/LlLrrMBMe3KHkvNtAV2UmRVxzLIg==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-page-break": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", - "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "dev": true, - "peerDependencies": { - "postcss": "^8" - } - }, - "node_modules/postcss-place": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-8.0.1.tgz", - "integrity": "sha512-Ow2LedN8sL4pq8ubukO77phSVt4QyCm35ZGCYXKvRFayAwcpgB0sjNJglDoTuRdUL32q/ZC1VkPBo0AOEr4Uiw==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-preset-env": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-8.5.1.tgz", - "integrity": "sha512-qhWnJJjP6ArLUINWJ38t6Aftxnv9NW6cXK0NuwcLCcRilbuw72dSFLkCVUJeCfHGgJiKzX+pnhkGiki0PEynWg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "dependencies": { - "@csstools/postcss-cascade-layers": "^3.0.1", - "@csstools/postcss-color-function": "^2.2.3", - "@csstools/postcss-color-mix-function": "^1.0.3", - "@csstools/postcss-font-format-keywords": "^2.0.2", - "@csstools/postcss-gradients-interpolation-method": "^3.0.6", - "@csstools/postcss-hwb-function": "^2.2.2", - "@csstools/postcss-ic-unit": "^2.0.4", - "@csstools/postcss-is-pseudo-class": "^3.2.1", - "@csstools/postcss-logical-float-and-clear": "^1.0.1", - "@csstools/postcss-logical-resize": "^1.0.1", - "@csstools/postcss-logical-viewport-units": "^1.0.3", - "@csstools/postcss-media-minmax": "^1.0.4", - "@csstools/postcss-media-queries-aspect-ratio-number-values": "^1.0.4", - "@csstools/postcss-nested-calc": "^2.0.2", - "@csstools/postcss-normalize-display-values": "^2.0.1", - "@csstools/postcss-oklab-function": "^2.2.3", - "@csstools/postcss-progressive-custom-properties": "^2.3.0", - "@csstools/postcss-relative-color-syntax": "^1.0.2", - "@csstools/postcss-scope-pseudo-class": "^2.0.2", - "@csstools/postcss-stepped-value-functions": "^2.1.1", - "@csstools/postcss-text-decoration-shorthand": "^2.2.4", - "@csstools/postcss-trigonometric-functions": "^2.1.1", - "@csstools/postcss-unset-value": "^2.0.1", - "autoprefixer": "^10.4.14", - "browserslist": "^4.21.9", - "css-blank-pseudo": "^5.0.2", - "css-has-pseudo": "^5.0.2", - "css-prefers-color-scheme": "^8.0.2", - "cssdb": "^7.6.0", - "postcss-attribute-case-insensitive": "^6.0.2", - "postcss-clamp": "^4.1.0", - "postcss-color-functional-notation": "^5.1.0", - "postcss-color-hex-alpha": "^9.0.2", - "postcss-color-rebeccapurple": "^8.0.2", - "postcss-custom-media": "^9.1.5", - "postcss-custom-properties": "^13.2.0", - "postcss-custom-selectors": "^7.1.3", - "postcss-dir-pseudo-class": "^7.0.2", - "postcss-double-position-gradients": "^4.0.4", - "postcss-focus-visible": "^8.0.2", - "postcss-focus-within": "^7.0.2", - "postcss-font-variant": "^5.0.0", - "postcss-gap-properties": "^4.0.1", - "postcss-image-set-function": "^5.0.2", - "postcss-initial": "^4.0.1", - "postcss-lab-function": "^5.2.3", - "postcss-logical": "^6.2.0", - "postcss-nesting": "^11.3.0", - "postcss-opacity-percentage": "^2.0.0", - "postcss-overflow-shorthand": "^4.0.1", - "postcss-page-break": "^3.0.4", - "postcss-place": "^8.0.1", - "postcss-pseudo-class-any-link": "^8.0.2", - "postcss-replace-overflow-wrap": "^4.0.0", - "postcss-selector-not": "^7.0.1", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-pseudo-class-any-link": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-8.0.2.tgz", - "integrity": "sha512-FYTIuRE07jZ2CW8POvctRgArQJ43yxhr5vLmImdKUvjFCkR09kh8pIdlCwdx/jbFm7MiW4QP58L4oOUv3grQYA==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-replace-overflow-wrap": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", - "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "dev": true, - "peerDependencies": { - "postcss": "^8.0.3" - } - }, - "node_modules/postcss-selector-not": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-7.0.1.tgz", - "integrity": "sha512-1zT5C27b/zeJhchN7fP0kBr16Cc61mu7Si9uWWLoA3Px/D9tIJPKchJCkUH3tPO5D0pCFmGeApAv8XpXBQJ8SQ==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", - "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", - "dev": true, - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/pretty-error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", - "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "dev": true, - "dependencies": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "dev": true, - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dev": true, - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dev": true, - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/rcedit": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/rcedit/-/rcedit-3.0.1.tgz", - "integrity": "sha512-XM0Jv40/y4hVAqj/MO70o/IWs4uOsaSoo2mLyk3klFDW+SStLnCtzuQu+1OBTIMGlM8CvaK9ftlYCp6DJ+cMsw==", - "dev": true, - "dependencies": { - "cross-spawn-windows-exe": "^1.1.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==", - "dev": true, - "dependencies": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==", - "dev": true, - "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg/node_modules/path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==", - "dev": true, - "dependencies": { - "pify": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", - "dev": true, - "dependencies": { - "resolve": "^1.20.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", - "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", - "dev": true, - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true - }, - "node_modules/regenerator-transform": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", - "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", - "dev": true, - "dependencies": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", - "dev": true, - "dependencies": { - "jsesc": "~0.5.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/renderkid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", - "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", - "dev": true, - "dependencies": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^6.0.1" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, - "node_modules/resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", - "dev": true, - "dependencies": { - "is-core-module": "^2.11.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", - "dev": true, - "dependencies": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-package": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-package/-/resolve-package-1.0.1.tgz", - "integrity": "sha512-rzB7NnQpOkPHBWFPP3prUMqOP6yg3HkRGgcvR+lDyvyHoY3fZLFLYDkPXh78SPVBAE6VTCk/V+j8we4djg6o4g==", - "dev": true, - "dependencies": { - "get-installed-path": "^2.0.3" - }, - "engines": { - "node": ">=4", - "npm": ">=2" - } - }, - "node_modules/responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", - "dependencies": { - "lowercase-keys": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", - "dev": true - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/roarr": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", - "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", - "optional": true, - "dependencies": { - "boolean": "^3.0.1", - "detect-node": "^2.0.4", - "globalthis": "^1.0.1", - "json-stringify-safe": "^5.0.1", - "semver-compare": "^1.0.0", - "sprintf-js": "^1.1.2" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", - "engines": { - "node": ">=10" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "devOptional": true - }, - "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", - "dev": true - }, - "node_modules/selfsigned": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", - "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", - "dev": true, - "dependencies": { - "node-forge": "^1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", - "optional": true - }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/serialize-error": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", - "optional": true, - "dependencies": { - "type-fest": "^0.13.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/serialize-error/node_modules/type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "optional": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", - "dev": true, - "dependencies": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-index/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/serve-index/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", - "dev": true, - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "dev": true - }, - "node_modules/serve-index/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/serve-index/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - }, - "node_modules/serve-index/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dev": true, - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/simple-html-tokenizer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/simple-html-tokenizer/-/simple-html-tokenizer-0.1.1.tgz", - "integrity": "sha512-Mc/gH3RvlKvB/gkp9XwgDKEWrSYyefIJPGG8Jk1suZms/rISdUuVEMx5O1WBnTWaScvxXDvGJrZQWblUmQHjkQ==", - "dev": true - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true, - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/snake-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", - "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "dev": true, - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", - "dev": true, - "dependencies": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" - } - }, - "node_modules/sockjs/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", - "dev": true, - "dependencies": { - "ip": "^2.0.0", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", - "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", - "dev": true, - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", - "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", - "dev": true - }, - "node_modules/spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "dev": true, - "dependencies": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "dev": true, - "dependencies": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "node_modules/sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", - "optional": true - }, - "node_modules/ssri": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.4.tgz", - "integrity": "sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ==", - "dev": true, - "dependencies": { - "minipass": "^5.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", - "engines": { - "node": "*" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-outer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", - "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-outer/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/style-loader": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.3.tgz", - "integrity": "sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw==", - "dev": true, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/sucrase": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", - "integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "7.1.6", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/sucrase/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/sucrase/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sudo-prompt": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz", - "integrity": "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==", - "dev": true - }, - "node_modules/sumchecker": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", - "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", - "dependencies": { - "debug": "^4.1.0" - }, - "engines": { - "node": ">= 8.0" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svg-inline-loader": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/svg-inline-loader/-/svg-inline-loader-0.8.2.tgz", - "integrity": "sha512-kbrcEh5n5JkypaSC152eGfGcnT4lkR0eSfvefaUJkLqgGjRQJyKDvvEE/CCv5aTSdfXuc+N98w16iAojhShI3g==", - "dev": true, - "dependencies": { - "loader-utils": "^1.1.0", - "object-assign": "^4.0.1", - "simple-html-tokenizer": "^0.1.1" - } - }, - "node_modules/svg-inline-loader/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/svg-inline-loader/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/svg-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", - "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", - "dev": true - }, - "node_modules/svgo": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.0.2.tgz", - "integrity": "sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==", - "dev": true, - "dependencies": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^5.1.0", - "css-tree": "^2.2.1", - "csso": "^5.0.5", - "picocolors": "^1.0.0" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/svgo" - } - }, - "node_modules/svgo/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/svgo/node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/svgo/node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dev": true, - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/svgo/node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dev": true, - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/svgo/node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dev": true, - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/svgo/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/tailwindcss": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz", - "integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==", - "dev": true, - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.2.12", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.18.2", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tailwindcss/node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/tar": { - "version": "6.1.15", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz", - "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", - "dev": true, - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar/node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/temp": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", - "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", - "dev": true, - "optional": true, - "dependencies": { - "mkdirp": "^0.5.1", - "rimraf": "~2.6.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/temp/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "optional": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/temp/node_modules/rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "optional": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/terser": { - "version": "5.18.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.18.1.tgz", - "integrity": "sha512-j1n0Ao919h/Ai5r43VAnfV/7azUYW43GPxK7qSATzrsERfW7+y2QW9Cp9ufnRF5CQUWbnLSo7UJokSWCqg4tsQ==", - "dev": true, - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.9", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", - "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.17", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.16.8" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "dev": true - }, - "node_modules/tiny-each-async": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/tiny-each-async/-/tiny-each-async-2.0.3.tgz", - "integrity": "sha512-5ROII7nElnAirvFn8g7H7MtpfV1daMcyfTGQwsn/x2VtyV+VPiO5CjReCJtWLvoKTDEDmZocf3cNPraiMnBXLA==", - "dev": true, - "optional": true - }, - "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "optional": true, - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, - "node_modules/tmp-promise": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", - "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", - "dev": true, - "optional": true, - "dependencies": { - "tmp": "^0.2.0" - } - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toggle-selection": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", - "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/trim-repeated": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", - "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/trim-repeated/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/triple-beam": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", - "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true - }, - "node_modules/ts-loader": { - "version": "9.4.3", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.3.tgz", - "integrity": "sha512-n3hBnm6ozJYzwiwt5YRiJZkzktftRpMiBApHaJPoWLA+qetQBAXkHqCLM6nwSdRDimqVtA5ocIkcTRLMTt7yzA==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.0.0", - "micromatch": "^4.0.0", - "semver": "^7.3.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "typescript": "*", - "webpack": "^5.0.0" - } - }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/tslib": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", - "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typescript": { - "version": "4.5.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", - "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unique-filename": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", - "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", - "dev": true, - "dependencies": { - "unique-slug": "^4.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/unique-slug": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", - "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", - "dev": true - }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url-loader": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", - "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", - "dev": true, - "dependencies": { - "loader-utils": "^2.0.0", - "mime-types": "^2.1.27", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "file-loader": "*", - "webpack": "^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "file-loader": { - "optional": true - } - } - }, - "node_modules/username": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/username/-/username-5.1.0.tgz", - "integrity": "sha512-PCKbdWw85JsYMvmCv5GH3kXmM66rCd9m1hBEDutPNv94b/pqCMT4NtcKyeWYvLFiE8b+ha1Jdl8XAaUdPn5QTg==", - "dev": true, - "dependencies": { - "execa": "^1.0.0", - "mem": "^4.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/username/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/username/node_modules/execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/username/node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/username/node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/username/node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", - "dev": true, - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/username/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/username/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/username/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/username/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/username/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", - "dev": true - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dev": true, - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dev": true, - "dependencies": { - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/webpack": { - "version": "5.88.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.0.tgz", - "integrity": "sha512-O3jDhG5e44qIBSi/P6KpcCcH7HD+nYIHVBhdWFxcLOcIGN8zGo5nqF3BjyNCxIh4p1vFdNnreZv2h2KkoAw3lw==", - "dev": true, - "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.0", - "@webassemblyjs/ast": "^1.11.5", - "@webassemblyjs/wasm-edit": "^1.11.5", - "@webassemblyjs/wasm-parser": "^1.11.5", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.15.0", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.7", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-cli": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", - "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", - "dev": true, - "dependencies": { - "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^2.1.1", - "@webpack-cli/info": "^2.0.2", - "@webpack-cli/serve": "^2.0.5", - "colorette": "^2.0.14", - "commander": "^10.0.1", - "cross-spawn": "^7.0.3", - "envinfo": "^7.7.3", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^3.1.1", - "rechoir": "^0.8.0", - "webpack-merge": "^5.7.3" - }, - "bin": { - "webpack-cli": "bin/cli.js" - }, - "engines": { - "node": ">=14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "5.x.x" - }, - "peerDependenciesMeta": { - "@webpack-cli/generators": { - "optional": true - }, - "webpack-bundle-analyzer": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/webpack-dev-middleware": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", - "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", - "dev": true, - "dependencies": { - "colorette": "^2.0.10", - "memfs": "^3.4.3", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/webpack-dev-middleware/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/webpack-dev-middleware/node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/webpack-dev-server": { - "version": "4.15.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz", - "integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==", - "dev": true, - "dependencies": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/serve-static": "^1.13.10", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.5", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^2.0.0", - "default-gateway": "^6.0.3", - "express": "^4.17.3", - "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "launch-editor": "^2.6.0", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.1.1", - "serve-index": "^1.9.1", - "sockjs": "^0.3.24", - "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", - "ws": "^8.13.0" - }, - "bin": { - "webpack-dev-server": "bin/webpack-dev-server.js" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.37.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - }, - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-dev-server/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack-dev-server/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/webpack-dev-server/node_modules/ipaddr.js": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", - "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/webpack-dev-server/node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/webpack-merge": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.9.0.tgz", - "integrity": "sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==", - "dev": true, - "dependencies": { - "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dev": true, - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dev": true, - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true - }, - "node_modules/winston": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.10.0.tgz", - "integrity": "sha512-nT6SIDaE9B7ZRO0u3UvdrimG0HkB7dSTAgInQnNR2SOPJ4bvq5q79+pXLftKmP52lJGW15+H5MCK0nM9D3KB/g==", - "dependencies": { - "@colors/colors": "1.5.0", - "@dabh/diagnostics": "^2.0.2", - "async": "^3.2.3", - "is-stream": "^2.0.0", - "logform": "^2.4.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "safe-stable-stringify": "^2.3.1", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.5.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/winston-daily-rotate-file": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-4.7.1.tgz", - "integrity": "sha512-7LGPiYGBPNyGHLn9z33i96zx/bd71pjBn9tqQzO3I4Tayv94WPmBNwKC7CO1wPHdP9uvu+Md/1nr6VSH9h0iaA==", - "dependencies": { - "file-stream-rotator": "^0.6.1", - "object-hash": "^2.0.1", - "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" - }, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "winston": "^3" - } - }, - "node_modules/winston-daily-rotate-file/node_modules/object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/winston-transport": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", - "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", - "dependencies": { - "logform": "^2.3.2", - "readable-stream": "^3.6.0", - "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 6.4.0" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "dev": true, - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xmlbuilder": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", - "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", - "dev": true, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/xterm": { - "version": "4.19.0", - "resolved": "https://registry.npmjs.org/xterm/-/xterm-4.19.0.tgz", - "integrity": "sha512-c3Cp4eOVsYY5Q839dR5IejghRPpxciGmLWWaP9g+ppfMeBChMeLa1DCA+pmX/jyDZ+zxFOmlJL/82qVdayVoGQ==", - "dev": true - }, - "node_modules/xterm-addon-fit": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.5.0.tgz", - "integrity": "sha512-DsS9fqhXHacEmsPxBJZvfj2la30Iz9xk+UKjhQgnYNkrUIN5CYLbw7WEfz117c7+S86S/tpHPfvNxJsF5/G8wQ==", - "dev": true, - "peerDependencies": { - "xterm": "^4.0.0" - } - }, - "node_modules/xterm-addon-search": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/xterm-addon-search/-/xterm-addon-search-0.8.2.tgz", - "integrity": "sha512-I1863mjn8P6uVrqm/X+btalVsqjAKLhnhpbP7SavAOpEkI1jJhbHU2UTp7NjeRtcKTks6UWk/ycgds5snDSejg==", - "dev": true, - "peerDependencies": { - "xterm": "^4.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yarn-or-npm": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/yarn-or-npm/-/yarn-or-npm-3.0.1.tgz", - "integrity": "sha512-fTiQP6WbDAh5QZAVdbMQkecZoahnbOjClTQhzv74WX5h2Uaidj1isf9FDes11TKtsZ0/ZVfZsqZ+O3x6aLERHQ==", - "dev": true, - "dependencies": { - "cross-spawn": "^6.0.5", - "pkg-dir": "^4.2.0" - }, - "bin": { - "yarn-or-npm": "bin/index.js", - "yon": "bin/index.js" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/yarn-or-npm/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/yarn-or-npm/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/yarn-or-npm/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/yarn-or-npm/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yarn-or-npm/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yarn-or-npm/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/macapp/package.json b/macapp/package.json deleted file mode 100644 index 088ec0b1..00000000 --- a/macapp/package.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "name": "ollama", - "productName": "Ollama", - "version": "0.0.0", - "description": "ollama", - "main": ".webpack/main", - "scripts": { - "start": "electron-forge start", - "package": "electron-forge package --arch universal", - "package:sign": "SIGN=1 electron-forge package --arch universal", - "make": "electron-forge make --arch universal", - "make:sign": "SIGN=1 electron-forge make --arch universal", - "publish": "SIGN=1 electron-forge publish", - "lint": "eslint --ext .ts,.tsx ." - }, - "keywords": [], - "author": { - "name": "Jeffrey Morgan", - "email": "jmorganca@gmail.com" - }, - "license": "MIT", - "devDependencies": { - "@babel/core": "^7.22.5", - "@babel/preset-react": "^7.22.5", - "@electron-forge/cli": "^6.2.1", - "@electron-forge/maker-deb": "^6.2.1", - "@electron-forge/maker-rpm": "^6.2.1", - "@electron-forge/maker-squirrel": "^6.2.1", - "@electron-forge/maker-zip": "^6.2.1", - "@electron-forge/plugin-auto-unpack-natives": "^6.2.1", - "@electron-forge/plugin-webpack": "^6.2.1", - "@electron-forge/publisher-github": "^6.2.1", - "@electron/universal": "^1.4.1", - "@svgr/webpack": "^8.0.1", - "@types/chmodr": "^1.0.0", - "@types/node": "^20.4.0", - "@types/react": "^18.2.14", - "@types/react-dom": "^18.2.6", - "@types/uuid": "^9.0.2", - "@typescript-eslint/eslint-plugin": "^5.60.0", - "@typescript-eslint/parser": "^5.60.0", - "@vercel/webpack-asset-relocator-loader": "^1.7.3", - "babel-loader": "^9.1.2", - "chmodr": "^1.2.0", - "copy-webpack-plugin": "^11.0.0", - "css-loader": "^6.8.1", - "electron": "25.9.2", - "eslint": "^8.43.0", - "eslint-plugin-import": "^2.27.5", - "fork-ts-checker-webpack-plugin": "^7.3.0", - "node-loader": "^2.0.0", - "postcss": "^8.4.24", - "postcss-import": "^15.1.0", - "postcss-loader": "^7.3.3", - "postcss-preset-env": "^8.5.1", - "style-loader": "^3.3.3", - "svg-inline-loader": "^0.8.2", - "tailwindcss": "^3.3.2", - "ts-loader": "^9.4.3", - "ts-node": "^10.9.1", - "typescript": "~4.5.4", - "url-loader": "^4.1.1", - "webpack": "^5.88.0", - "webpack-cli": "^5.1.4", - "webpack-dev-server": "^4.15.1" - }, - "dependencies": { - "@electron/remote": "^2.0.10", - "@heroicons/react": "^2.0.18", - "@segment/analytics-node": "^1.0.0", - "copy-to-clipboard": "^3.3.3", - "electron-squirrel-startup": "^1.0.0", - "electron-store": "^8.1.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "uuid": "^9.0.0", - "winston": "^3.10.0", - "winston-daily-rotate-file": "^4.7.1" - } -} diff --git a/macapp/postcss.config.js b/macapp/postcss.config.js deleted file mode 100644 index d0ffab7e..00000000 --- a/macapp/postcss.config.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - plugins: { - 'postcss-import': {}, - tailwindcss: {}, - autoprefixer: {}, - }, -} diff --git a/macapp/src/app.css b/macapp/src/app.css deleted file mode 100644 index c22da2f5..00000000 --- a/macapp/src/app.css +++ /dev/null @@ -1,34 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -html, -body { - background: transparent; -} - -.drag { - -webkit-app-region: drag; -} - -.no-drag { - -webkit-app-region: no-drag; -} - -.blink { - -webkit-animation: 1s blink step-end infinite; - -moz-animation: 1s blink step-end infinite; - -ms-animation: 1s blink step-end infinite; - -o-animation: 1s blink step-end infinite; - animation: 1s blink step-end infinite; -} - -@keyframes blink { - from, - to { - color: transparent; - } - 50% { - color: black; - } -} diff --git a/macapp/src/app.tsx b/macapp/src/app.tsx deleted file mode 100644 index 449fc851..00000000 --- a/macapp/src/app.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import { useState } from 'react' -import copy from 'copy-to-clipboard' -import { CheckIcon, DocumentDuplicateIcon } from '@heroicons/react/24/outline' -import Store from 'electron-store' -import { getCurrentWindow, app } from '@electron/remote' - -import { install } from './install' -import OllamaIcon from './ollama.svg' - -const store = new Store() - -enum Step { - WELCOME = 0, - CLI, - FINISH, -} - -export default function () { - const [step, setStep] = useState(Step.WELCOME) - const [commandCopied, setCommandCopied] = useState(false) - - const command = 'ollama run llama3.2' - - return ( -

-
- {step === Step.WELCOME && ( - <> -
-

Welcome to Ollama

-

- Let's get you up and running with your own large language models. -

- -
-
- -
- - )} - {step === Step.CLI && ( - <> -
-

Install the command line

-
> ollama
-
- -

- You will be prompted for administrator access -

-
-
- - )} - {step === Step.FINISH && ( - <> -
-

Run your first model

-
-
-
-                    {command}
-                  
- -
-

- Run this command in your favorite terminal. -

-
- -
- - )} -
-
- ) -} diff --git a/macapp/src/declarations.d.ts b/macapp/src/declarations.d.ts deleted file mode 100644 index a97fbda7..00000000 --- a/macapp/src/declarations.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module '*.svg' { - const content: string - export default content -} diff --git a/macapp/src/index.html b/macapp/src/index.html deleted file mode 100644 index 9a636cbf..00000000 --- a/macapp/src/index.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - -
- - diff --git a/macapp/src/index.ts b/macapp/src/index.ts deleted file mode 100644 index a5d04d5f..00000000 --- a/macapp/src/index.ts +++ /dev/null @@ -1,302 +0,0 @@ -import { spawn, ChildProcess } from 'child_process' -import { app, autoUpdater, dialog, Tray, Menu, BrowserWindow, MenuItemConstructorOptions, nativeTheme } from 'electron' -import Store from 'electron-store' -import winston from 'winston' -import 'winston-daily-rotate-file' -import * as path from 'path' - -import { v4 as uuidv4 } from 'uuid' -import { installed } from './install' - -require('@electron/remote/main').initialize() - -if (require('electron-squirrel-startup')) { - app.quit() -} - -const store = new Store() - -let welcomeWindow: BrowserWindow | null = null - -declare const MAIN_WINDOW_WEBPACK_ENTRY: string - -const logger = winston.createLogger({ - transports: [ - new winston.transports.Console(), - new winston.transports.File({ - filename: path.join(app.getPath('home'), '.ollama', 'logs', 'server.log'), - maxsize: 1024 * 1024 * 20, - maxFiles: 5, - }), - ], - format: winston.format.printf(info => info.message), -}) - -app.on('ready', () => { - const gotTheLock = app.requestSingleInstanceLock() - if (!gotTheLock) { - app.exit(0) - return - } - - app.on('second-instance', () => { - if (app.hasSingleInstanceLock()) { - app.releaseSingleInstanceLock() - } - - if (proc) { - proc.off('exit', restart) - proc.kill() - } - - app.exit(0) - }) - - app.focus({ steal: true }) - - init() -}) - -function firstRunWindow() { - // Create the browser window. - welcomeWindow = new BrowserWindow({ - width: 400, - height: 500, - frame: false, - fullscreenable: false, - resizable: false, - movable: true, - show: false, - webPreferences: { - nodeIntegration: true, - contextIsolation: false, - }, - }) - - require('@electron/remote/main').enable(welcomeWindow.webContents) - - welcomeWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY) - welcomeWindow.on('ready-to-show', () => welcomeWindow.show()) - welcomeWindow.on('closed', () => { - if (process.platform === 'darwin') { - app.dock.hide() - } - }) -} - -let tray: Tray | null = null -let updateAvailable = false -const assetPath = app.isPackaged ? process.resourcesPath : path.join(__dirname, '..', '..', 'assets') - -function trayIconPath() { - return nativeTheme.shouldUseDarkColors - ? updateAvailable - ? path.join(assetPath, 'iconDarkUpdateTemplate.png') - : path.join(assetPath, 'iconDarkTemplate.png') - : updateAvailable - ? path.join(assetPath, 'iconUpdateTemplate.png') - : path.join(assetPath, 'iconTemplate.png') -} - -function updateTrayIcon() { - if (tray) { - tray.setImage(trayIconPath()) - } -} - -function updateTray() { - const updateItems: MenuItemConstructorOptions[] = [ - { label: 'An update is available', enabled: false }, - { - label: 'Restart to update', - click: () => autoUpdater.quitAndInstall(), - }, - { type: 'separator' }, - ] - - const menu = Menu.buildFromTemplate([ - ...(updateAvailable ? updateItems : []), - { role: 'quit', label: 'Quit Ollama', accelerator: 'Command+Q' }, - ]) - - if (!tray) { - tray = new Tray(trayIconPath()) - } - - tray.setToolTip(updateAvailable ? 'An update is available' : 'Ollama') - tray.setContextMenu(menu) - tray.setImage(trayIconPath()) - - nativeTheme.off('updated', updateTrayIcon) - nativeTheme.on('updated', updateTrayIcon) -} - -let proc: ChildProcess = null - -function server() { - const binary = app.isPackaged - ? path.join(process.resourcesPath, 'ollama') - : path.resolve(process.cwd(), '..', 'ollama') - - proc = spawn(binary, ['serve']) - - proc.stdout.on('data', data => { - logger.info(data.toString().trim()) - }) - - proc.stderr.on('data', data => { - logger.error(data.toString().trim()) - }) - - proc.on('exit', restart) -} - -function restart() { - setTimeout(server, 1000) -} - -app.on('before-quit', () => { - if (proc) { - proc.off('exit', restart) - proc.kill('SIGINT') // send SIGINT signal to the server, which also stops any loaded llms - } -}) - -const updateURL = `https://ollama.com/api/update?os=${process.platform}&arch=${ - process.arch -}&version=${app.getVersion()}&id=${id()}` - -let latest = '' -async function isNewReleaseAvailable() { - try { - const response = await fetch(updateURL) - - if (!response.ok) { - return false - } - - if (response.status === 204) { - return false - } - - const data = await response.json() - - const url = data?.url - if (!url) { - return false - } - - if (latest === url) { - return false - } - - latest = url - - return true - } catch (error) { - logger.error(`update check failed - ${error}`) - return false - } -} - -async function checkUpdate() { - const available = await isNewReleaseAvailable() - if (available) { - logger.info('checking for update') - autoUpdater.checkForUpdates() - } -} - -function init() { - if (app.isPackaged) { - checkUpdate() - setInterval(() => { - checkUpdate() - }, 60 * 60 * 1000) - } - - updateTray() - - if (process.platform === 'darwin') { - if (app.isPackaged) { - if (!app.isInApplicationsFolder()) { - const chosen = dialog.showMessageBoxSync({ - type: 'question', - buttons: ['Move to Applications', 'Do Not Move'], - message: 'Ollama works best when run from the Applications directory.', - defaultId: 0, - cancelId: 1, - }) - - if (chosen === 0) { - try { - app.moveToApplicationsFolder({ - conflictHandler: conflictType => { - if (conflictType === 'existsAndRunning') { - dialog.showMessageBoxSync({ - type: 'info', - message: 'Cannot move to Applications directory', - detail: - 'Another version of Ollama is currently running from your Applications directory. Close it first and try again.', - }) - } - return true - }, - }) - return - } catch (e) { - logger.error(`[Move to Applications] Failed to move to applications folder - ${e.message}}`) - } - } - } - } - } - - server() - - if (store.get('first-time-run') && installed()) { - if (process.platform === 'darwin') { - app.dock.hide() - } - - app.setLoginItemSettings({ openAtLogin: app.getLoginItemSettings().openAtLogin }) - return - } - - // This is the first run or the CLI is no longer installed - app.setLoginItemSettings({ openAtLogin: true }) - firstRunWindow() -} - -// Quit when all windows are closed, except on macOS. There, it's common -// for applications and their menu bar to stay active until the user quits -// explicitly with Cmd + Q. -app.on('window-all-closed', () => { - if (process.platform !== 'darwin') { - app.quit() - } -}) - -function id(): string { - const id = store.get('id') as string - - if (id) { - return id - } - - const uuid = uuidv4() - store.set('id', uuid) - return uuid -} - -autoUpdater.setFeedURL({ url: updateURL }) - -autoUpdater.on('error', e => { - logger.error(`update check failed - ${e.message}`) - console.error(`update check failed - ${e.message}`) -}) - -autoUpdater.on('update-downloaded', () => { - updateAvailable = true - updateTray() -}) diff --git a/macapp/src/install.ts b/macapp/src/install.ts deleted file mode 100644 index d8036542..00000000 --- a/macapp/src/install.ts +++ /dev/null @@ -1,21 +0,0 @@ -import * as fs from 'fs' -import { exec as cbExec } from 'child_process' -import * as path from 'path' -import { promisify } from 'util' - -const app = process && process.type === 'renderer' ? require('@electron/remote').app : require('electron').app -const ollama = app.isPackaged ? path.join(process.resourcesPath, 'ollama') : path.resolve(process.cwd(), '..', 'ollama') -const exec = promisify(cbExec) -const symlinkPath = '/usr/local/bin/ollama' - -export function installed() { - return fs.existsSync(symlinkPath) && fs.readlinkSync(symlinkPath) === ollama -} - -export async function install() { - const command = `do shell script "mkdir -p ${path.dirname( - symlinkPath - )} && ln -F -s \\"${ollama}\\" \\"${symlinkPath}\\"" with administrator privileges` - - await exec(`osascript -e '${command}'`) -} diff --git a/macapp/src/ollama.svg b/macapp/src/ollama.svg deleted file mode 100644 index 6e9fb283..00000000 --- a/macapp/src/ollama.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/macapp/src/preload.ts b/macapp/src/preload.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/macapp/src/renderer.tsx b/macapp/src/renderer.tsx deleted file mode 100644 index d4712dd1..00000000 --- a/macapp/src/renderer.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import App from './app' -import './app.css' -import { createRoot } from 'react-dom/client' - -const container = document.getElementById('app') -const root = createRoot(container) -root.render() diff --git a/macapp/tailwind.config.js b/macapp/tailwind.config.js deleted file mode 100644 index 12e08fc6..00000000 --- a/macapp/tailwind.config.js +++ /dev/null @@ -1,6 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'], - theme: {}, - plugins: [], -} diff --git a/macapp/tsconfig.json b/macapp/tsconfig.json deleted file mode 100644 index 407b5ea3..00000000 --- a/macapp/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "compilerOptions": { - "target": "ES6", - "allowJs": true, - "module": "commonjs", - "skipLibCheck": true, - "esModuleInterop": true, - "noImplicitAny": true, - "sourceMap": true, - "baseUrl": ".", - "outDir": "dist", - "moduleResolution": "node", - "resolveJsonModule": true, - "paths": { - "*": ["node_modules/*"] - }, - "jsx": "react-jsx" - }, - "include": ["src/**/*"] -} diff --git a/macapp/webpack.main.config.ts b/macapp/webpack.main.config.ts deleted file mode 100644 index 51160862..00000000 --- a/macapp/webpack.main.config.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { Configuration } from 'webpack' - -import { rules } from './webpack.rules' -import { plugins } from './webpack.plugins' - -export const mainConfig: Configuration = { - /** - * This is the main entry point for your application, it's the first file - * that runs in the main process. - */ - entry: './src/index.ts', - // Put your normal webpack config below here - module: { - rules, - }, - plugins, - resolve: { - extensions: ['.js', '.ts', '.jsx', '.tsx', '.css', '.json'], - }, -} diff --git a/macapp/webpack.plugins.ts b/macapp/webpack.plugins.ts deleted file mode 100644 index 3f9ce147..00000000 --- a/macapp/webpack.plugins.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type IForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin' -import { DefinePlugin } from 'webpack' - -// eslint-disable-next-line @typescript-eslint/no-var-requires -const ForkTsCheckerWebpackPlugin: typeof IForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin') - -export const plugins = [ - new ForkTsCheckerWebpackPlugin({ - logger: 'webpack-infrastructure', - }), - new DefinePlugin({ - 'process.env.TELEMETRY_WRITE_KEY': JSON.stringify(process.env.TELEMETRY_WRITE_KEY), - }), -] diff --git a/macapp/webpack.renderer.config.ts b/macapp/webpack.renderer.config.ts deleted file mode 100644 index 2ef22b2f..00000000 --- a/macapp/webpack.renderer.config.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { Configuration } from 'webpack' - -import { rules } from './webpack.rules' -import { plugins } from './webpack.plugins' - -rules.push({ - test: /\.css$/, - use: [{ loader: 'style-loader' }, { loader: 'css-loader' }, { loader: 'postcss-loader' }], -}) - -export const rendererConfig: Configuration = { - module: { - rules, - }, - plugins, - resolve: { - extensions: ['.js', '.ts', '.jsx', '.tsx', '.css'], - }, -} diff --git a/macapp/webpack.rules.ts b/macapp/webpack.rules.ts deleted file mode 100644 index a4b109b9..00000000 --- a/macapp/webpack.rules.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { ModuleOptions } from 'webpack' - -export const rules: Required['rules'] = [ - // Add support for native node modules - { - // We're specifying native_modules in the test because the asset relocator loader generates a - // "fake" .node file which is really a cjs file. - test: /native_modules[/\\].+\.node$/, - use: 'node-loader', - }, - { - test: /[/\\]node_modules[/\\].+\.(m?js|node)$/, - parser: { amd: false }, - use: { - loader: '@vercel/webpack-asset-relocator-loader', - options: { - outputAssetBase: 'native_modules', - }, - }, - }, - { - test: /\.tsx?$/, - exclude: /(node_modules|\.webpack)/, - use: { - loader: 'ts-loader', - options: { - transpileOnly: true, - }, - }, - }, - { - test: /\.svg$/, - use: ['@svgr/webpack'], - }, -] From 684a9a8c5a01acfe13ee4a55a7dc7aff69f6b17a Mon Sep 17 00:00:00 2001 From: Kowyo Date: Thu, 13 Nov 2025 12:49:33 +0800 Subject: [PATCH 27/32] docs: fix typo (VSCode -> VS Code) (#13072) --- README.md | 4 ++-- docs/faq.mdx | 2 +- docs/integrations/vscode.mdx | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index cf1d04d1..422f2b12 100644 --- a/README.md +++ b/README.md @@ -366,7 +366,7 @@ See the [API documentation](./docs/api.md) for all endpoints. - [PartCAD](https://github.com/openvmp/partcad/) (CAD model generation with OpenSCAD and CadQuery) - [Ollama4j Web UI](https://github.com/ollama4j/ollama4j-web-ui) - Java-based Web UI for Ollama built with Vaadin, Spring Boot, and Ollama4j - [PyOllaMx](https://github.com/kspviswa/pyOllaMx) - macOS application capable of chatting with both Ollama and Apple MLX models. -- [Cline](https://github.com/cline/cline) - Formerly known as Claude Dev is a VSCode extension for multi-file/whole-repo coding +- [Cline](https://github.com/cline/cline) - Formerly known as Claude Dev is a VS Code extension for multi-file/whole-repo coding - [Cherry Studio](https://github.com/kangfenmao/cherry-studio) (Desktop client with Ollama support) - [ConfiChat](https://github.com/1runeberg/confichat) (Lightweight, standalone, multi-platform, and privacy-focused LLM chat interface with optional encryption) - [Archyve](https://github.com/nickthecook/archyve) (RAG-enabling document library) @@ -398,7 +398,7 @@ See the [API documentation](./docs/api.md) for all endpoints. - [aidful-ollama-model-delete](https://github.com/AidfulAI/aidful-ollama-model-delete) (User interface for simplified model cleanup) - [Perplexica](https://github.com/ItzCrazyKns/Perplexica) (An AI-powered search engine & an open-source alternative to Perplexity AI) - [Ollama Chat WebUI for Docker ](https://github.com/oslook/ollama-webui) (Support for local docker deployment, lightweight ollama webui) -- [AI Toolkit for Visual Studio Code](https://aka.ms/ai-tooklit/ollama-docs) (Microsoft-official VSCode extension to chat, test, evaluate models with Ollama support, and use them in your AI applications.) +- [AI Toolkit for Visual Studio Code](https://aka.ms/ai-tooklit/ollama-docs) (Microsoft-official VS Code extension to chat, test, evaluate models with Ollama support, and use them in your AI applications.) - [MinimalNextOllamaChat](https://github.com/anilkay/MinimalNextOllamaChat) (Minimal Web UI for Chat and Model Control) - [Chipper](https://github.com/TilmanGriesel/chipper) AI interface for tinkerers (Ollama, Haystack RAG, Python) - [ChibiChat](https://github.com/CosmicEventHorizon/ChibiChat) (Kotlin-based Android app to chat with Ollama and Koboldcpp API endpoints) diff --git a/docs/faq.mdx b/docs/faq.mdx index f6ae513b..d9398e9d 100644 --- a/docs/faq.mdx +++ b/docs/faq.mdx @@ -223,7 +223,7 @@ Refer to the section [above](#how-do-i-configure-ollama-server) for how to set e ## How can I use Ollama in Visual Studio Code? -There is already a large collection of plugins available for VSCode as well as other editors that leverage Ollama. See the list of [extensions & plugins](https://github.com/ollama/ollama#extensions--plugins) at the bottom of the main repository readme. +There is already a large collection of plugins available for VS Code as well as other editors that leverage Ollama. See the list of [extensions & plugins](https://github.com/ollama/ollama#extensions--plugins) at the bottom of the main repository readme. ## How do I use Ollama with GPU acceleration in Docker? diff --git a/docs/integrations/vscode.mdx b/docs/integrations/vscode.mdx index c68f9199..6f407a88 100644 --- a/docs/integrations/vscode.mdx +++ b/docs/integrations/vscode.mdx @@ -4,7 +4,7 @@ title: VS Code ## Install -Install [VSCode](https://code.visualstudio.com/download). +Install [VS Code](https://code.visualstudio.com/download). ## Usage with Ollama @@ -12,7 +12,7 @@ Install [VSCode](https://code.visualstudio.com/download).
VSCode chat Sidebar
@@ -20,7 +20,7 @@ Install [VSCode](https://code.visualstudio.com/download).
VSCode model picker
@@ -28,7 +28,7 @@ Install [VSCode](https://code.visualstudio.com/download).
VSCode model options dropdown
From 482bec824f3027e9049ac2a981ce36c954e67882 Mon Sep 17 00:00:00 2001 From: nicole pardal <109545900+npardal@users.noreply.github.com> Date: Thu, 13 Nov 2025 13:24:13 -0800 Subject: [PATCH 28/32] embeddings: added cli command to embedding docs (#12993) --- docs/capabilities/embeddings.mdx | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/capabilities/embeddings.mdx b/docs/capabilities/embeddings.mdx index 99a57748..7d364200 100644 --- a/docs/capabilities/embeddings.mdx +++ b/docs/capabilities/embeddings.mdx @@ -13,9 +13,23 @@ Embeddings turn text into numeric vectors you can store in a vector database, se ## Generate embeddings -Use `/api/embed` with a single string. - + + Generate embeddings directly from the command line: + + ```shell + ollama run embeddinggemma "Hello world" + ``` + + You can also pipe text to generate embeddings: + + ```shell + echo "Hello world" | ollama run embeddinggemma + ``` + + Output is a JSON array. + + ```shell curl -X POST http://localhost:11434/api/embed \ From b48083f33f6784374106f7fcfc29f682af74a6ed Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Thu, 13 Nov 2025 13:28:21 -0800 Subject: [PATCH 29/32] ml: add slice operation (#12870) * slice * chunk, chunksections --- ml/backend.go | 4 + ml/backend/ggml/ggml.go | 63 ++++ ml/backend/ggml/ggml_test.go | 707 ++++++++++++++++++++++++++++++++++- 3 files changed, 773 insertions(+), 1 deletion(-) diff --git a/ml/backend.go b/ml/backend.go index b07039e2..c6fadb7f 100644 --- a/ml/backend.go +++ b/ml/backend.go @@ -198,6 +198,10 @@ type Tensor interface { Copy(ctx Context, t2 Tensor) Tensor Duplicate(ctx Context) Tensor + Slice(ctx Context, dim, low, high, step int) Tensor + Chunk(ctx Context, dim int, size int) []Tensor + ChunkSections(ctx Context, dim int, sections ...int) []Tensor + TopK(ctx Context, k int) Tensor Argsort(ctx Context) Tensor Mean(ctx Context) Tensor diff --git a/ml/backend/ggml/ggml.go b/ml/backend/ggml/ggml.go index 5fa0a9ec..e18d2f38 100644 --- a/ml/backend/ggml/ggml.go +++ b/ml/backend/ggml/ggml.go @@ -1738,3 +1738,66 @@ func (t *Tensor) Clamp(ctx ml.Context, min, max float32) ml.Tensor { t: C.ggml_clamp(ctx.(*Context).ctx, t.t, C.float(min), C.float(max)), } } + +// Slice returns a view of the tensor sliced along dim from low to high in step steps. +// Slice panics if the dimension is invalid or the slice parameters are out of range. +// If dim=0 and step>1, the tensor is a copy rather than a view to ensure proper shape. +func (t *Tensor) Slice(ctx ml.Context, dim int, low, high, step int) ml.Tensor { + if dim < 0 || dim >= C.GGML_MAX_DIMS { + panic("invalid dimension") + } else if low < 0 || high > t.Dim(dim) || low >= high || step < 1 { + panic("invalid slice parameters") + } + + if dim == 0 && step > 1 { + // dim=0,step>1 is a special case so handle it here first + return t.View(ctx, + low*t.Stride(0), 1, + step*t.Stride(0), (high-low+1)/step, + t.Stride(1), t.Dim(1), + // preserve dim 3 by merging it into dim 2 + t.Stride(2), t.Dim(2)*t.Dim(3), + ).Contiguous(ctx, (high-low+1)/step, t.Dim(1), t.Dim(2), t.Dim(3)) + } + + args := []int{ + low * t.Stride(dim), t.Dim(0), + t.Stride(1), t.Dim(1), + t.Stride(2), t.Dim(2), + t.Stride(3), t.Dim(3), + } + + if step == 1 { + args[dim*2+1] = high - low + return t.View(ctx, args[0], args[1:]...) + } else { + args[dim*2] = step * t.Stride(dim) + args[dim*2+1] = (high - low + 1) / step + return t.View(ctx, args[0], args[1:]...) + } +} + +// Chunk the tensor into chunk sized tensors along dim. Each sub-tensor is a view of +// the original. +func (t *Tensor) Chunk(ctx ml.Context, dim, chunk int) []ml.Tensor { + sections := make([]int, 0, t.Dim(dim)/chunk+1) + for rest := t.Dim(dim); rest > 0; rest -= chunk { + sections = append(sections, min(chunk, rest)) + } + return t.ChunkSections(ctx, dim, sections...) +} + +// ChunkSections split the tensor into section sized tensors along dim. Each sub-tensor is a +// view of the original. The size of the dim must equal the sum of sections. +func (t *Tensor) ChunkSections(ctx ml.Context, dim int, sections ...int) []ml.Tensor { + var offset int + s := make([]ml.Tensor, len(sections)) + for i, section := range sections { + s[i] = t.Slice(ctx, dim, offset, offset+section, 1) + offset += section + } + if offset != t.Dim(dim) { + panic("sections do not sum to tensor dimension") + } + return s +} diff --git a/ml/backend/ggml/ggml_test.go b/ml/backend/ggml/ggml_test.go index 31dfdb7b..efd3a455 100644 --- a/ml/backend/ggml/ggml_test.go +++ b/ml/backend/ggml/ggml_test.go @@ -2,6 +2,7 @@ package ggml import ( "errors" + "fmt" "os" "testing" @@ -368,10 +369,714 @@ func TestPermute(t *testing.T) { for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { ctx := setup(t) - got := tt.input(ctx).Permute(ctx, tt.shape...).Contiguous(ctx) + got := tt.input(ctx).Permute(ctx, tt.shape...) + got = got.Contiguous(ctx) if diff := cmp.Diff(tt.want(ctx), got, EquateTensors(ctx)); diff != "" { t.Errorf("Permute() result mismatch (-want +got):\n%s", diff) } }) } } + +func TestSlice(t *testing.T) { + cases := []struct { + dim int + low int + high int + step int + input func(ml.Context) ml.Tensor + want func(ml.Context) ml.Tensor + }{ + { + dim: 0, low: 1, high: 3, step: 1, + input: func(ctx ml.Context) ml.Tensor { + return ctx.Arange(0, 4*4*4*4, 1, ml.DTypeF32).Reshape(ctx, 4, 4, 4, 4) + }, + want: func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{ + 1, 2, + 5, 6, + 9, 10, + 13, 14, + + 17, 18, + 21, 22, + 25, 26, + 29, 30, + + 33, 34, + 37, 38, + 41, 42, + 45, 46, + + 49, 50, + 53, 54, + 57, 58, + 61, 62, + + 65, 66, + 69, 70, + 73, 74, + 77, 78, + + 81, 82, + 85, 86, + 89, 90, + 93, 94, + + 97, 98, + 101, 102, + 105, 106, + 109, 110, + + 113, 114, + 117, 118, + 121, 122, + 125, 126, + + 129, 130, + 133, 134, + 137, 138, + 141, 142, + + 145, 146, + 149, 150, + 153, 154, + 157, 158, + + 161, 162, + 165, 166, + 169, 170, + 173, 174, + + 177, 178, + 181, 182, + 185, 186, + 189, 190, + + 193, 194, + 197, 198, + 201, 202, + 205, 206, + + 209, 210, + 213, 214, + 217, 218, + 221, 222, + + 225, 226, + 229, 230, + 233, 234, + 237, 238, + + 241, 242, + 245, 246, + 249, 250, + 253, 254, + }, 2, 4, 4, 4) + }, + }, + { + dim: 1, low: 1, high: 3, step: 1, + input: func(ctx ml.Context) ml.Tensor { + return ctx.Arange(0, 4*4*4*4, 1, ml.DTypeF32).Reshape(ctx, 4, 4, 4, 4) + }, + want: func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{ + 4, 5, 6, 7, + 8, 9, 10, 11, + + 20, 21, 22, 23, + 24, 25, 26, 27, + + 36, 37, 38, 39, + 40, 41, 42, 43, + + 52, 53, 54, 55, + 56, 57, 58, 59, + + 68, 69, 70, 71, + 72, 73, 74, 75, + + 84, 85, 86, 87, + 88, 89, 90, 91, + + 100, 101, 102, 103, + 104, 105, 106, 107, + + 116, 117, 118, 119, + 120, 121, 122, 123, + + 132, 133, 134, 135, + 136, 137, 138, 139, + + 148, 149, 150, 151, + 152, 153, 154, 155, + + 164, 165, 166, 167, + 168, 169, 170, 171, + + 180, 181, 182, 183, + 184, 185, 186, 187, + + 196, 197, 198, 199, + 200, 201, 202, 203, + + 212, 213, 214, 215, + 216, 217, 218, 219, + + 228, 229, 230, 231, + 232, 233, 234, 235, + + 244, 245, 246, 247, + 248, 249, 250, 251, + }, 4, 2, 4, 4) + }, + }, + { + dim: 2, low: 1, high: 3, step: 1, + input: func(ctx ml.Context) ml.Tensor { + return ctx.Arange(0, 4*4*4*4, 1, ml.DTypeF32).Reshape(ctx, 4, 4, 4, 4) + }, + want: func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{ + 16, 17, 18, 19, + 20, 21, 22, 23, + 24, 25, 26, 27, + 28, 29, 30, 31, + + 32, 33, 34, 35, + 36, 37, 38, 39, + 40, 41, 42, 43, + 44, 45, 46, 47, + + 80, 81, 82, 83, + 84, 85, 86, 87, + 88, 89, 90, 91, + 92, 93, 94, 95, + + 96, 97, 98, 99, + 100, 101, 102, 103, + 104, 105, 106, 107, + 108, 109, 110, 111, + + 144, 145, 146, 147, + 148, 149, 150, 151, + 152, 153, 154, 155, + 156, 157, 158, 159, + + 160, 161, 162, 163, + 164, 165, 166, 167, + 168, 169, 170, 171, + 172, 173, 174, 175, + + 208, 209, 210, 211, + 212, 213, 214, 215, + 216, 217, 218, 219, + 220, 221, 222, 223, + + 224, 225, 226, 227, + 228, 229, 230, 231, + 232, 233, 234, 235, + 236, 237, 238, 239, + }, 4, 4, 2, 4) + }, + }, + { + dim: 3, low: 1, high: 3, step: 1, + input: func(ctx ml.Context) ml.Tensor { + return ctx.Arange(0, 4*4*4*4, 1, ml.DTypeF32).Reshape(ctx, 4, 4, 4, 4) + }, + want: func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{ + 64, 65, 66, 67, + 68, 69, 70, 71, + 72, 73, 74, 75, + 76, 77, 78, 79, + + 80, 81, 82, 83, + 84, 85, 86, 87, + 88, 89, 90, 91, + 92, 93, 94, 95, + + 96, 97, 98, 99, + 100, 101, 102, 103, + 104, 105, 106, 107, + 108, 109, 110, 111, + + 112, 113, 114, 115, + 116, 117, 118, 119, + 120, 121, 122, 123, + 124, 125, 126, 127, + + 128, 129, 130, 131, + 132, 133, 134, 135, + 136, 137, 138, 139, + 140, 141, 142, 143, + + 144, 145, 146, 147, + 148, 149, 150, 151, + 152, 153, 154, 155, + 156, 157, 158, 159, + + 160, 161, 162, 163, + 164, 165, 166, 167, + 168, 169, 170, 171, + 172, 173, 174, 175, + + 176, 177, 178, 179, + 180, 181, 182, 183, + 184, 185, 186, 187, + 188, 189, 190, 191, + }, 4, 4, 4, 2) + }, + }, + { + dim: 0, low: 0, high: 4, step: 2, + input: func(ctx ml.Context) ml.Tensor { + return ctx.Arange(0, 4*4*4*4, 1, ml.DTypeF32).Reshape(ctx, 4, 4, 4, 4) + }, + want: func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{ + 0, 2, + 4, 6, + 8, 10, + 12, 14, + + 16, 18, + 20, 22, + 24, 26, + 28, 30, + + 32, 34, + 36, 38, + 40, 42, + 44, 46, + + 48, 50, + 52, 54, + 56, 58, + 60, 62, + + 64, 66, + 68, 70, + 72, 74, + 76, 78, + + 80, 82, + 84, 86, + 88, 90, + 92, 94, + + 96, 98, + 100, 102, + 104, 106, + 108, 110, + + 112, 114, + 116, 118, + 120, 122, + 124, 126, + + 128, 130, + 132, 134, + 136, 138, + 140, 142, + + 144, 146, + 148, 150, + 152, 154, + 156, 158, + + 160, 162, + 164, 166, + 168, 170, + 172, 174, + + 176, 178, + 180, 182, + 184, 186, + 188, 190, + + 192, 194, + 196, 198, + 200, 202, + 204, 206, + + 208, 210, + 212, 214, + 216, 218, + 220, 222, + + 224, 226, + 228, 230, + 232, 234, + 236, 238, + + 240, 242, + 244, 246, + 248, 250, + 252, 254, + }, 2, 4, 4, 4) + }, + }, + { + dim: 1, low: 0, high: 4, step: 2, + input: func(ctx ml.Context) ml.Tensor { + return ctx.Arange(0, 4*4*4*4, 1, ml.DTypeF32).Reshape(ctx, 4, 4, 4, 4) + }, + want: func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{ + 0, 1, 2, 3, + 8, 9, 10, 11, + + 16, 17, 18, 19, + 24, 25, 26, 27, + + 32, 33, 34, 35, + 40, 41, 42, 43, + + 48, 49, 50, 51, + 56, 57, 58, 59, + + 64, 65, 66, 67, + 72, 73, 74, 75, + + 80, 81, 82, 83, + 88, 89, 90, 91, + + 96, 97, 98, 99, + 104, 105, 106, 107, + + 112, 113, 114, 115, + 120, 121, 122, 123, + + 128, 129, 130, 131, + 136, 137, 138, 139, + + 144, 145, 146, 147, + 152, 153, 154, 155, + + 160, 161, 162, 163, + 168, 169, 170, 171, + + 176, 177, 178, 179, + 184, 185, 186, 187, + + 192, 193, 194, 195, + 200, 201, 202, 203, + + 208, 209, 210, 211, + 216, 217, 218, 219, + + 224, 225, 226, 227, + 232, 233, 234, 235, + + 240, 241, 242, 243, + 248, 249, 250, 251, + }, 4, 2, 4, 4) + }, + }, + { + dim: 2, low: 0, high: 4, step: 2, + input: func(ctx ml.Context) ml.Tensor { + return ctx.Arange(0, 4*4*4*4, 1, ml.DTypeF32).Reshape(ctx, 4, 4, 4, 4) + }, + want: func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{ + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11, + 12, 13, 14, 15, + + 32, 33, 34, 35, + 36, 37, 38, 39, + 40, 41, 42, 43, + 44, 45, 46, 47, + + 64, 65, 66, 67, + 68, 69, 70, 71, + 72, 73, 74, 75, + 76, 77, 78, 79, + + 96, 97, 98, 99, + 100, 101, 102, 103, + 104, 105, 106, 107, + 108, 109, 110, 111, + + 128, 129, 130, 131, + 132, 133, 134, 135, + 136, 137, 138, 139, + 140, 141, 142, 143, + + 160, 161, 162, 163, + 164, 165, 166, 167, + 168, 169, 170, 171, + 172, 173, 174, 175, + + 192, 193, 194, 195, + 196, 197, 198, 199, + 200, 201, 202, 203, + 204, 205, 206, 207, + + 224, 225, 226, 227, + 228, 229, 230, 231, + 232, 233, 234, 235, + 236, 237, 238, 239, + }, 4, 4, 2, 4) + }, + }, + { + dim: 3, low: 0, high: 4, step: 2, + input: func(ctx ml.Context) ml.Tensor { + return ctx.Arange(0, 4*4*4*4, 1, ml.DTypeF32).Reshape(ctx, 4, 4, 4, 4) + }, + want: func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{ + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11, + 12, 13, 14, 15, + + 16, 17, 18, 19, + 20, 21, 22, 23, + 24, 25, 26, 27, + 28, 29, 30, 31, + + 32, 33, 34, 35, + 36, 37, 38, 39, + 40, 41, 42, 43, + 44, 45, 46, 47, + + 48, 49, 50, 51, + 52, 53, 54, 55, + 56, 57, 58, 59, + 60, 61, 62, 63, + + 128, 129, 130, 131, + 132, 133, 134, 135, + 136, 137, 138, 139, + 140, 141, 142, 143, + + 144, 145, 146, 147, + 148, 149, 150, 151, + 152, 153, 154, 155, + 156, 157, 158, 159, + + 160, 161, 162, 163, + 164, 165, 166, 167, + 168, 169, 170, 171, + 172, 173, 174, 175, + + 176, 177, 178, 179, + 180, 181, 182, 183, + 184, 185, 186, 187, + 188, 189, 190, 191, + }, 4, 4, 4, 2) + }, + }, + } + + for _, tt := range cases { + name := fmt.Sprintf("dim=%d,low=%d,high=%d,step=%d", tt.dim, tt.low, tt.high, tt.step) + t.Run(name, func(t *testing.T) { + ctx := setup(t) + got := tt.input(ctx).Slice(ctx, tt.dim, tt.low, tt.high, tt.step) + got = got.Contiguous(ctx) + if diff := cmp.Diff(tt.want(ctx), got, EquateTensors(ctx)); diff != "" { + t.Errorf("Slice() result mismatch (-want +got):\n%s", diff) + } + }) + } +} + +func TestSplitSections(t *testing.T) { + cases := []struct { + dim int + sections []int + input func(ml.Context) ml.Tensor + want []func(ml.Context) ml.Tensor + }{ + { + dim: 0, sections: []int{1, 1, 1}, + input: func(ctx ml.Context) ml.Tensor { + return ctx.Arange(0, 12, 1, ml.DTypeF32).Reshape(ctx, 3, 4) + }, + want: []func(ml.Context) ml.Tensor{ + func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{0, 3, 6, 9}, 1, 4) + }, + func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{1, 4, 7, 10}, 1, 4) + }, + func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{2, 5, 8, 11}, 1, 4) + }, + }, + }, + { + dim: 1, sections: []int{1, 3}, + input: func(ctx ml.Context) ml.Tensor { + return ctx.Arange(0, 12, 1, ml.DTypeF32).Reshape(ctx, 3, 4) + }, + want: []func(ml.Context) ml.Tensor{ + func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{0, 1, 2}, 3, 1) + }, + func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{ + 3, 4, 5, + 6, 7, 8, + 9, 10, 11, + }, 3, 3) + }, + }, + }, + { + dim: 0, sections: []int{2, 2}, + input: func(ctx ml.Context) ml.Tensor { + return ctx.Arange(0, 12, 1, ml.DTypeF32).Reshape(ctx, 4, 3) + }, + want: []func(ml.Context) ml.Tensor{ + func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{ + 0, 1, + 4, 5, + 8, 9, + }, 2, 3) + }, + func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{ + 2, 3, + 6, 7, + 10, 11, + }, 2, 3) + }, + }, + }, + { + dim: 1, sections: []int{1, 2}, + input: func(ctx ml.Context) ml.Tensor { + return ctx.Arange(0, 12, 1, ml.DTypeF32).Reshape(ctx, 4, 3) + }, + want: []func(ml.Context) ml.Tensor{ + func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{0, 1, 2, 3}, 4, 1) + }, + func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{ + 4, 5, 6, 7, + 8, 9, 10, 11, + }, 4, 2) + }, + }, + }, + } + + for _, tt := range cases { + t.Run(fmt.Sprintf("sections=%v", tt.sections), func(t *testing.T) { + ctx := setup(t) + got := tt.input(ctx).ChunkSections(ctx, tt.dim, tt.sections...) + + for i := range got { + got[i] = got[i].Contiguous(ctx) + } + + ctx.Forward(got...).Compute(got...) + for i, want := range tt.want { + if diff := cmp.Diff(want(ctx), got[i], EquateTensors(ctx)); diff != "" { + t.Errorf("SplitSections() section %d mismatch (-want +got):\n%s", i, diff) + } + } + }) + } +} + +func TestChunk(t *testing.T) { + cases := []struct { + dim int + chunk int + input func(ml.Context) ml.Tensor + want []func(ml.Context) ml.Tensor + }{ + { + dim: 0, chunk: 1, + input: func(ctx ml.Context) ml.Tensor { + return ctx.Arange(0, 12, 1, ml.DTypeF32).Reshape(ctx, 3, 4) + }, + want: []func(ml.Context) ml.Tensor{ + func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{0, 3, 6, 9}, 1, 4) + }, + func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{1, 4, 7, 10}, 1, 4) + }, + func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{2, 5, 8, 11}, 1, 4) + }, + }, + }, + { + dim: 1, chunk: 2, + input: func(ctx ml.Context) ml.Tensor { + return ctx.Arange(0, 12, 1, ml.DTypeF32).Reshape(ctx, 3, 4) + }, + want: []func(ml.Context) ml.Tensor{ + func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{ + 0, 1, 2, + 3, 4, 5, + }, 3, 2) + }, + func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{ + 6, 7, 8, + 9, 10, 11, + }, 3, 2) + }, + }, + }, + { + dim: 0, chunk: 2, + input: func(ctx ml.Context) ml.Tensor { + return ctx.Arange(0, 12, 1, ml.DTypeF32).Reshape(ctx, 3, 4) + }, + want: []func(ml.Context) ml.Tensor{ + func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{ + 0, 1, + 3, 4, + 6, 7, + 9, 10, + }, 2, 4) + }, + func(ctx ml.Context) ml.Tensor { + return ctx.FromFloats([]float32{ + 2, + 5, + 8, + 11, + }, 1, 4) + }, + }, + }, + } + + for _, tt := range cases { + t.Run(fmt.Sprintf("dim=%d,chunk=%d", tt.dim, tt.chunk), func(t *testing.T) { + ctx := setup(t) + got := tt.input(ctx).Chunk(ctx, tt.dim, tt.chunk) + + for i := range got { + got[i] = got[i].Contiguous(ctx) + } + + ctx.Forward(got...).Compute(got...) + for i, want := range tt.want { + if diff := cmp.Diff(want(ctx), got[i], EquateTensors(ctx)); diff != "" { + t.Errorf("Split() section %d mismatch (-want +got):\n%s", i, diff) + } + } + }) + } +} From c1149875234a51aa1e5e60b74f3807f5982c60fa Mon Sep 17 00:00:00 2001 From: Parth Sareen Date: Thu, 13 Nov 2025 13:49:25 -0800 Subject: [PATCH 30/32] logprob: add bytes to logprobs (#13068) --- api/types.go | 3 + integration/api_test.go | 25 +++ server/logprob.go | 15 ++ server/routes_generate_test.go | 269 +++++++++++++++++++++++++++++++++ 4 files changed, 312 insertions(+) diff --git a/api/types.go b/api/types.go index d5788d54..d8467629 100644 --- a/api/types.go +++ b/api/types.go @@ -366,6 +366,9 @@ type TokenLogprob struct { // Logprob is the log probability of this token. Logprob float64 `json:"logprob"` + + // Bytes contains the raw byte representation of the token + Bytes []int `json:"bytes,omitempty"` } // Logprob contains log probability information for a generated token. diff --git a/integration/api_test.go b/integration/api_test.go index 839e14d7..66d1e9e9 100644 --- a/integration/api_test.go +++ b/integration/api_test.go @@ -14,6 +14,23 @@ import ( "github.com/ollama/ollama/api" ) +func assertBytesMatchToken(t *testing.T, label, token string, ints []int) { + t.Helper() + + raw := []byte(token) + if len(ints) != len(raw) { + t.Errorf("%s expected %d bytes for token %q, got %d (%v)", label, len(raw), token, len(ints), ints) + return + } + + for i, b := range raw { + if ints[i] != int(b) { + t.Errorf("%s byte[%d] mismatch for token %q: got %d want %d", label, i, token, ints[i], int(b)) + return + } + } +} + func TestAPIGenerate(t *testing.T) { initialTimeout := 60 * time.Second streamTimeout := 30 * time.Second @@ -466,6 +483,7 @@ func TestAPIGenerateLogprobs(t *testing.T) { if lp.Logprob > 0 { t.Errorf("logprob[%d] has positive logprob %f (should be <= 0)", i, lp.Logprob) } + assertBytesMatchToken(t, fmt.Sprintf("generate logprob[%d]", i), lp.Token, lp.Bytes) // Check top_logprobs if requested if test.topLogprobs > 0 { @@ -482,6 +500,9 @@ func TestAPIGenerateLogprobs(t *testing.T) { t.Errorf("logprob[%d].top_logprobs not sorted: %f < %f", i, lp.TopLogprobs[j-1].Logprob, lp.TopLogprobs[j].Logprob) } } + for j, top := range lp.TopLogprobs { + assertBytesMatchToken(t, fmt.Sprintf("generate logprob[%d].top[%d]", i, j), top.Token, top.Bytes) + } } else if len(lp.TopLogprobs) > 0 { t.Errorf("logprob[%d] has top_logprobs but none were requested", i) } @@ -544,11 +565,15 @@ func TestAPIChatLogprobs(t *testing.T) { if lp.Logprob > 0 { t.Errorf("logprob[%d] has positive logprob %f", i, lp.Logprob) } + assertBytesMatchToken(t, fmt.Sprintf("chat logprob[%d]", i), lp.Token, lp.Bytes) if len(lp.TopLogprobs) == 0 { t.Errorf("logprob[%d] expected top_logprobs but got none", i) } if len(lp.TopLogprobs) > 3 { t.Errorf("logprob[%d] has %d top_logprobs, expected max 3", i, len(lp.TopLogprobs)) } + for j, top := range lp.TopLogprobs { + assertBytesMatchToken(t, fmt.Sprintf("chat logprob[%d].top[%d]", i, j), top.Token, top.Bytes) + } } } diff --git a/server/logprob.go b/server/logprob.go index 51996c2a..4a6e1408 100644 --- a/server/logprob.go +++ b/server/logprob.go @@ -12,6 +12,7 @@ func toAPILogprobs(logprobs []llm.Logprob) []api.Logprob { result[i] = api.Logprob{ TokenLogprob: api.TokenLogprob{ Token: lp.Token, + Bytes: stringToByteInts(lp.Token), Logprob: lp.Logprob, }, } @@ -20,6 +21,7 @@ func toAPILogprobs(logprobs []llm.Logprob) []api.Logprob { for j, tlp := range lp.TopLogprobs { result[i].TopLogprobs[j] = api.TokenLogprob{ Token: tlp.Token, + Bytes: stringToByteInts(tlp.Token), Logprob: tlp.Logprob, } } @@ -27,3 +29,16 @@ func toAPILogprobs(logprobs []llm.Logprob) []api.Logprob { } return result } + +func stringToByteInts(s string) []int { + if s == "" { + return nil + } + + raw := []byte(s) + ints := make([]int, len(raw)) + for i, b := range raw { + ints[i] = int(b) + } + return ints +} diff --git a/server/routes_generate_test.go b/server/routes_generate_test.go index a6be3bf3..a9931ea2 100644 --- a/server/routes_generate_test.go +++ b/server/routes_generate_test.go @@ -1220,6 +1220,139 @@ func TestGenerateLogprobs(t *testing.T) { t.Errorf("mismatch (-got +want):\n%s", diff) } }) + + t.Run("returns logprob bytes when requested", func(t *testing.T) { + gin.SetMode(gin.TestMode) + + mock := &mockRunner{} + expectedPrimary := llm.TokenLogprob{ + Token: "Hi", + Logprob: -0.01, + } + expectedAlternatives := []llm.TokenLogprob{ + { + Token: "Hello", + Logprob: -0.25, + }, + { + Token: "Hey", + Logprob: -0.5, + }, + } + + mock.CompletionFn = func(ctx context.Context, r llm.CompletionRequest, fn func(llm.CompletionResponse)) error { + fn(llm.CompletionResponse{ + Content: "Hi", + Done: true, + DoneReason: llm.DoneReasonStop, + PromptEvalCount: 1, + PromptEvalDuration: 1, + EvalCount: 1, + EvalDuration: 1, + Logprobs: []llm.Logprob{ + { + TokenLogprob: expectedPrimary, + TopLogprobs: expectedAlternatives, + }, + }, + }) + return nil + } + + s := &Server{ + sched: &Scheduler{ + pendingReqCh: make(chan *LlmRequest, 1), + finishedReqCh: make(chan *LlmRequest, 1), + expiredCh: make(chan *runnerRef, 1), + unloadedCh: make(chan any, 1), + loaded: make(map[string]*runnerRef), + newServerFn: newMockServer(mock), + getGpuFn: getGpuFn, + getSystemInfoFn: getSystemInfoFn, + waitForRecovery: 250 * time.Millisecond, + loadFn: func(req *LlmRequest, _ *ggml.GGML, _ ml.SystemInfo, _ []ml.DeviceInfo, _ bool) bool { + req.successCh <- &runnerRef{llama: mock} + return false + }, + }, + } + + go s.sched.Run(t.Context()) + + _, digest := createBinFile(t, ggml.KV{ + "general.architecture": "llama", + "llama.block_count": uint32(1), + "llama.context_length": uint32(8192), + "llama.embedding_length": uint32(4096), + "llama.attention.head_count": uint32(32), + "llama.attention.head_count_kv": uint32(8), + "tokenizer.ggml.tokens": []string{""}, + "tokenizer.ggml.scores": []float32{0}, + "tokenizer.ggml.token_type": []int32{0}, + }, []*ggml.Tensor{ + {Name: "token_embd.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + {Name: "blk.0.attn_norm.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + {Name: "blk.0.ffn_down.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + {Name: "blk.0.ffn_gate.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + {Name: "blk.0.ffn_up.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + {Name: "blk.0.ffn_norm.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + {Name: "blk.0.attn_k.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + {Name: "blk.0.attn_output.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + {Name: "blk.0.attn_q.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + {Name: "blk.0.attn_v.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + {Name: "output.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + }) + + if w := createRequest(t, s.CreateHandler, api.CreateRequest{ + Model: "test-logprob-bytes", + Files: map[string]string{"file.gguf": digest}, + Template: `{{ .Prompt }}`, + Stream: &stream, + }); w.Code != http.StatusOK { + t.Fatalf("expected status 200, got %d", w.Code) + } + + stream := false + w := createRequest(t, s.GenerateHandler, api.GenerateRequest{ + Model: "test-logprob-bytes", + Prompt: "Hi", + Stream: &stream, + Logprobs: true, + TopLogprobs: len(expectedAlternatives), + }) + + if w.Code != http.StatusOK { + t.Fatalf("expected status 200, got %d", w.Code) + } + + var resp api.GenerateResponse + if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { + t.Fatalf("failed to decode response: %v", err) + } + + if len(resp.Logprobs) != 1 { + t.Fatalf("expected 1 logprob entry, got %d", len(resp.Logprobs)) + } + + expectedPrimaryBytes := stringToByteInts(expectedPrimary.Token) + expectedAlternativesBytes := make([][]int, len(expectedAlternatives)) + for i, alternative := range expectedAlternatives { + expectedAlternativesBytes[i] = stringToByteInts(alternative.Token) + } + if diff := cmp.Diff(expectedPrimaryBytes, resp.Logprobs[0].Bytes); diff != "" { + t.Fatalf("primary token bytes mismatch (-want +got):\n%s", diff) + } + + if len(resp.Logprobs[0].TopLogprobs) != len(expectedAlternatives) { + t.Fatalf("expected %d top logprobs, got %d", len(expectedAlternatives), len(resp.Logprobs[0].TopLogprobs)) + } + + for i, top := range resp.Logprobs[0].TopLogprobs { + if diff := cmp.Diff(expectedAlternativesBytes[i], top.Bytes); diff != "" { + t.Fatalf("top logprob[%d] bytes mismatch (-want +got):\n%s", i, diff) + } + } + }) } func TestChatLogprobs(t *testing.T) { @@ -1262,6 +1395,142 @@ func TestChatLogprobs(t *testing.T) { t.Errorf("mismatch (-got +want):\n%s", diff) } }) + + t.Run("returns logprob bytes when requested", func(t *testing.T) { + gin.SetMode(gin.TestMode) + + mock := &mockRunner{} + expectedPrimary := llm.TokenLogprob{ + Token: "Hi", + Logprob: -0.02, + } + expectedAlternatives := []llm.TokenLogprob{ + { + Token: "Hello", + Logprob: -0.3, + }, + { + Token: "Hey", + Logprob: -0.45, + }, + } + expectedPrimaryBytes := stringToByteInts(expectedPrimary.Token) + expectedAlternativesBytes := make([][]int, len(expectedAlternatives)) + for i, alternative := range expectedAlternatives { + expectedAlternativesBytes[i] = stringToByteInts(alternative.Token) + } + + mock.CompletionFn = func(ctx context.Context, r llm.CompletionRequest, fn func(llm.CompletionResponse)) error { + fn(llm.CompletionResponse{ + Content: "Hi", + Done: true, + DoneReason: llm.DoneReasonStop, + PromptEvalCount: 1, + PromptEvalDuration: 1, + EvalCount: 1, + EvalDuration: 1, + Logprobs: []llm.Logprob{ + { + TokenLogprob: expectedPrimary, + TopLogprobs: expectedAlternatives, + }, + }, + }) + return nil + } + + s := &Server{ + sched: &Scheduler{ + pendingReqCh: make(chan *LlmRequest, 1), + finishedReqCh: make(chan *LlmRequest, 1), + expiredCh: make(chan *runnerRef, 1), + unloadedCh: make(chan any, 1), + loaded: make(map[string]*runnerRef), + newServerFn: newMockServer(mock), + getGpuFn: getGpuFn, + getSystemInfoFn: getSystemInfoFn, + waitForRecovery: 250 * time.Millisecond, + loadFn: func(req *LlmRequest, _ *ggml.GGML, _ ml.SystemInfo, _ []ml.DeviceInfo, _ bool) bool { + req.successCh <- &runnerRef{llama: mock} + return false + }, + }, + } + + go s.sched.Run(t.Context()) + + _, digest := createBinFile(t, ggml.KV{ + "general.architecture": "llama", + "llama.block_count": uint32(1), + "llama.context_length": uint32(8192), + "llama.embedding_length": uint32(4096), + "llama.attention.head_count": uint32(32), + "llama.attention.head_count_kv": uint32(8), + "tokenizer.ggml.tokens": []string{""}, + "tokenizer.ggml.scores": []float32{0}, + "tokenizer.ggml.token_type": []int32{0}, + }, []*ggml.Tensor{ + {Name: "token_embd.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + {Name: "blk.0.attn_norm.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + {Name: "blk.0.ffn_down.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + {Name: "blk.0.ffn_gate.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + {Name: "blk.0.ffn_up.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + {Name: "blk.0.ffn_norm.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + {Name: "blk.0.attn_k.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + {Name: "blk.0.attn_output.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + {Name: "blk.0.attn_q.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + {Name: "blk.0.attn_v.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + {Name: "output.weight", Shape: []uint64{1}, WriterTo: bytes.NewReader(make([]byte, 4))}, + }) + + if w := createRequest(t, s.CreateHandler, api.CreateRequest{ + Model: "test-chat-logprob-bytes", + Files: map[string]string{"file.gguf": digest}, + Template: `{{- range .Messages }}{{ .Role }}: {{ .Content }} +{{ end }}`, + Stream: &stream, + }); w.Code != http.StatusOK { + t.Fatalf("expected status 200, got %d", w.Code) + } + + stream := false + w := createRequest(t, s.ChatHandler, api.ChatRequest{ + Model: "test-chat-logprob-bytes", + Messages: []api.Message{ + {Role: "user", Content: "Say hi"}, + }, + Stream: &stream, + Logprobs: true, + TopLogprobs: len(expectedAlternatives), + }) + + if w.Code != http.StatusOK { + t.Fatalf("expected status 200, got %d", w.Code) + } + + var resp api.ChatResponse + if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { + t.Fatalf("failed to decode response: %v", err) + } + + if len(resp.Logprobs) != 1 { + t.Fatalf("expected 1 logprob entry, got %d", len(resp.Logprobs)) + } + + if diff := cmp.Diff(expectedPrimaryBytes, resp.Logprobs[0].Bytes); diff != "" { + t.Fatalf("primary token bytes mismatch (-want +got):\n%s", diff) + } + + if len(resp.Logprobs[0].TopLogprobs) != len(expectedAlternatives) { + t.Fatalf("expected %d top logprobs, got %d", len(expectedAlternatives), len(resp.Logprobs[0].TopLogprobs)) + } + + for i, top := range resp.Logprobs[0].TopLogprobs { + if diff := cmp.Diff(expectedAlternativesBytes[i], top.Bytes); diff != "" { + t.Fatalf("top logprob[%d] bytes mismatch (-want +got):\n%s", i, diff) + } + } + }) } func TestChatWithPromptEndingInThinkTag(t *testing.T) { From 333203d871339414d266a75e2134e87022ff110f Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Thu, 13 Nov 2025 15:20:12 -0800 Subject: [PATCH 31/32] chore: update models to use slice/chunk/chunksections (#12934) * use slice/chunks * bert * llama4 * gemma3n * gptoss * mistral3 * qwen3vl * qwen25vl * deepseek2 * remove unused ops --- convert/convert_gptoss.go | 9 ++++-- ml/backend.go | 3 -- ml/backend/ggml/ggml.go | 28 ------------------- ml/nn/pooling/pooling.go | 5 ++-- model/models/bert/embed.go | 2 +- model/models/deepseek2/model.go | 40 ++++++++++----------------- model/models/gemma3n/model_text.go | 14 +++++----- model/models/gptoss/model.go | 38 ++++--------------------- model/models/llama4/model.go | 8 ++---- model/models/llama4/model_vision.go | 22 ++++++--------- model/models/mistral3/model_vision.go | 6 ++-- model/models/qwen25vl/model_vision.go | 6 ++-- model/models/qwen3vl/model_vision.go | 13 +++++---- 13 files changed, 59 insertions(+), 135 deletions(-) diff --git a/convert/convert_gptoss.go b/convert/convert_gptoss.go index 5338df21..d7bfb361 100644 --- a/convert/convert_gptoss.go +++ b/convert/convert_gptoss.go @@ -110,9 +110,12 @@ func (m *gptossModel) Tensors(ts []Tensor) []*ggml.Tensor { for name, mxfp4 := range mxfp4s { dims := mxfp4.blocks.Shape() + if !strings.HasSuffix(name, ".weight") { + name = name + ".weight" + } if strings.Contains(name, "ffn_down_exps") { out = append(out, &ggml.Tensor{ - Name: name + ".weight", + Name: name, Kind: uint32(ggml.TensorTypeMXFP4), Shape: []uint64{dims[0], dims[1], dims[2] * dims[3] * 2}, WriterTo: mxfp4, @@ -121,12 +124,12 @@ func (m *gptossModel) Tensors(ts []Tensor) []*ggml.Tensor { // gate_up_exps is interleaved, need to split into gate_exps and up_exps // e.g. gate_exps, up_exps = gate_up_exps[:, 0::2, ...], gate_up_exps[:, 1::2, ...] out = append(out, &ggml.Tensor{ - Name: strings.Replace(name, "gate_up", "gate", 1) + ".weight", + Name: strings.Replace(name, "gate_up", "gate", 1), Kind: uint32(ggml.TensorTypeMXFP4), Shape: []uint64{dims[0], dims[1] / 2, dims[2] * dims[3] * 2}, WriterTo: mxfp4.slice(1, 0, int(dims[1]), 2), }, &ggml.Tensor{ - Name: strings.Replace(name, "gate_up", "up", 1) + ".weight", + Name: strings.Replace(name, "gate_up", "up", 1), Kind: uint32(ggml.TensorTypeMXFP4), Shape: []uint64{dims[0], dims[1] / 2, dims[2] * dims[3] * 2}, WriterTo: mxfp4.slice(1, 1, int(dims[1]), 2), diff --git a/ml/backend.go b/ml/backend.go index c6fadb7f..36557e62 100644 --- a/ml/backend.go +++ b/ml/backend.go @@ -146,7 +146,6 @@ type Tensor interface { FromFloats([]float32) FromInts([]int32) - Neg(ctx Context) Tensor Add(ctx Context, t2 Tensor) Tensor Sub(ctx Context, t2 Tensor) Tensor Mul(ctx Context, t2 Tensor) Tensor @@ -185,7 +184,6 @@ type Tensor interface { View(ctx Context, offset int, shape ...int) Tensor Permute(ctx Context, shape ...int) Tensor Contiguous(ctx Context, shape ...int) Tensor - Set(ctx Context, t2 Tensor, offset int, strides ...int) Tensor Pad(ctx Context, shape ...int) Tensor @@ -209,7 +207,6 @@ type Tensor interface { Stddev(ctx Context) Tensor Sqr(ctx Context) Tensor Sqrt(ctx Context) Tensor - Clamp(ctx Context, min, max float32) Tensor } // ScaledDotProductAttention implements a fused attention diff --git a/ml/backend/ggml/ggml.go b/ml/backend/ggml/ggml.go index e18d2f38..2aa72190 100644 --- a/ml/backend/ggml/ggml.go +++ b/ml/backend/ggml/ggml.go @@ -1137,13 +1137,6 @@ func (t *Tensor) Cast(ctx ml.Context, dtype ml.DType) ml.Tensor { } } -func (t *Tensor) Neg(ctx ml.Context) ml.Tensor { - return &Tensor{ - b: t.b, - t: C.ggml_neg(ctx.(*Context).ctx, t.t), - } -} - func (t *Tensor) Add(ctx ml.Context, t2 ml.Tensor) ml.Tensor { return &Tensor{ b: t.b, @@ -1632,20 +1625,6 @@ func (t *Tensor) AvgPool2D(ctx ml.Context, k, s int, p float32) ml.Tensor { } } -func (t *Tensor) Set(ctx ml.Context, t2 ml.Tensor, offset int, strides ...int) ml.Tensor { - var tt *C.struct_ggml_tensor - switch len(strides) { - case 0: - tt = C.ggml_set_1d(ctx.(*Context).ctx, t.t, t2.(*Tensor).t, C.size_t(offset)) - case 1: - tt = C.ggml_set_2d(ctx.(*Context).ctx, t.t, t2.(*Tensor).t, C.size_t(offset), C.size_t(strides[0])) - default: - panic("unsupported number of dimensions") - } - - return &Tensor{b: t.b, t: tt} -} - func (t *Tensor) ScaledDotProductAttention(ctx ml.Context, key, value, mask, sinks ml.Tensor, scale float64) ml.Tensor { var kqMask *C.struct_ggml_tensor if mask != nil { @@ -1732,13 +1711,6 @@ func (t *Tensor) Sqrt(ctx ml.Context) ml.Tensor { } } -func (t *Tensor) Clamp(ctx ml.Context, min, max float32) ml.Tensor { - return &Tensor{ - b: t.b, - t: C.ggml_clamp(ctx.(*Context).ctx, t.t, C.float(min), C.float(max)), - } -} - // Slice returns a view of the tensor sliced along dim from low to high in step steps. // Slice panics if the dimension is invalid or the slice parameters are out of range. // If dim=0 and step>1, the tensor is a copy rather than a view to ensure proper shape. diff --git a/ml/nn/pooling/pooling.go b/ml/nn/pooling/pooling.go index 63b63b3a..47af8746 100644 --- a/ml/nn/pooling/pooling.go +++ b/ml/nn/pooling/pooling.go @@ -32,10 +32,9 @@ func (t Type) Forward(ctx ml.Context, hiddenStates ml.Tensor) ml.Tensor { hiddenStates = hiddenStates.Permute(ctx, 1, 0, 2, 3).Contiguous(ctx).Mean(ctx) return hiddenStates.Permute(ctx, 1, 0, 2, 3).Contiguous(ctx) case TypeCLS: - return hiddenStates.View(ctx, 0, hiddenStates.Dim(0)) + return hiddenStates.Slice(ctx, 1, 0, 1, 1) case TypeLast: - hiddenStates = hiddenStates.View(ctx, (hiddenStates.Dim(1)-1)*hiddenStates.Stride(1), hiddenStates.Dim(0)) - return hiddenStates + return hiddenStates.Slice(ctx, 1, hiddenStates.Dim(1)-1, hiddenStates.Dim(1), 1) default: panic("unknown pooling type") } diff --git a/model/models/bert/embed.go b/model/models/bert/embed.go index 2d78710f..f2dd1deb 100644 --- a/model/models/bert/embed.go +++ b/model/models/bert/embed.go @@ -29,7 +29,7 @@ type Model struct { // Forward implements model.Model. func (m *Model) Forward(ctx ml.Context, batch input.Batch) (ml.Tensor, error) { hiddenStates := m.TokenEmbedding.Forward(ctx, batch.Inputs) - hiddenStates = hiddenStates.Add(ctx, m.TypeEmbedding.Weight.View(ctx, 0, m.hiddenSize)) + hiddenStates = hiddenStates.Add(ctx, m.TypeEmbedding.Weight.Slice(ctx, 1, 0, 1, 1)) hiddenStates = hiddenStates.Add(ctx, m.PositionEmbedding.Forward(ctx, ctx.Input().FromInts(batch.Positions, len(batch.Positions)))) hiddenStates = m.TokenEmbeddingNorm.Forward(ctx, hiddenStates, m.eps) diff --git a/model/models/deepseek2/model.go b/model/models/deepseek2/model.go index cfd579ca..68b12cd9 100644 --- a/model/models/deepseek2/model.go +++ b/model/models/deepseek2/model.go @@ -78,44 +78,31 @@ func (attn *Attention) Forward(ctx ml.Context, hiddenStates, positions ml.Tensor } query = query.Reshape(ctx, query.Dim(0)/opts.numHeads, opts.numHeads, seqLength) - - qPass := query.View(ctx, 0, - opts.qkNopeHeadDim, query.Stride(1), - query.Dim(1), query.Stride(2), - query.Dim(2)) - - qRot := query.View(ctx, opts.qkNopeHeadDim*query.Stride(0), - opts.qkRopeHeadDim, query.Stride(1), - query.Dim(1), query.Stride(2), - query.Dim(2)) + queryChunks := query.ChunkSections(ctx, 0, opts.qkNopeHeadDim, opts.qkRopeHeadDim) compressedKV := attn.KVA.Forward(ctx, hiddenStates) - - kPass := compressedKV.View(ctx, 0, opts.kvLoraRank, compressedKV.Stride(1), compressedKV.Dim(1)) - kRot := compressedKV.View(ctx, opts.kvLoraRank*compressedKV.Stride(0), - opts.qkRopeHeadDim, compressedKV.Stride(1), - 1, compressedKV.Stride(1), - compressedKV.Dim(1)) + kPass := compressedKV.Slice(ctx, 0, 0, opts.kvLoraRank, 1) + kRot := compressedKV.View(ctx, + opts.kvLoraRank*compressedKV.Stride(0), opts.qkRopeHeadDim, + compressedKV.Stride(1), 1, + compressedKV.Stride(1), compressedKV.Dim(1), + ) kPass = attn.KVANorm.Forward(ctx, kPass, opts.eps) kPass = attn.KVB.Forward(ctx, kPass) kv := kPass.Reshape(ctx, kPass.Dim(0)/opts.numKVHeads, opts.numKVHeads, seqLength) - kPass = kv.View(ctx, 0, opts.kqNopeHeadDim, kv.Stride(1), kv.Dim(1), kv.Stride(2), kv.Dim(2)) - value := kv.View(ctx, opts.kqNopeHeadDim*kv.Stride(0), - opts.vHeadDim, kv.Stride(1), - kv.Dim(1), kv.Stride(2), - kv.Dim(2)).Contiguous(ctx) + kvChunks := kv.ChunkSections(ctx, 0, opts.kqNopeHeadDim, opts.vHeadDim) - qRot = fast.RoPE(ctx, qRot, positions, opts.qkRopeHeadDim, opts.ropeBase, 1./opts.ropeScale, opts.RoPEOptions()...) + qRot := fast.RoPE(ctx, queryChunks[1], positions, opts.qkRopeHeadDim, opts.ropeBase, 1./opts.ropeScale, opts.RoPEOptions()...) kRot = fast.RoPE(ctx, kRot, positions, opts.qkRopeHeadDim, opts.ropeBase, 1./opts.ropeScale, opts.RoPEOptions()...) - kRot = kRot.Repeat(ctx, 1, qPass.Dim(1)) + kRot = kRot.Repeat(ctx, 1, queryChunks[0].Dim(1)) - query = qRot.Concat(ctx, qPass, 0) - key := kRot.Concat(ctx, kPass, 0) + query = qRot.Concat(ctx, queryChunks[0], 0) + key := kRot.Concat(ctx, kvChunks[0], 0) - attention := nn.Attention(ctx, query, key, value, opts.kqScale, cache) + attention := nn.Attention(ctx, query, key, kvChunks[1], opts.kqScale, cache) attention = attention.Reshape(ctx, attention.Dim(0)*attention.Dim(1), seqLength) return attn.Output.Forward(ctx, attention) } @@ -142,6 +129,7 @@ func (moe *sparse) Moe(ctx ml.Context, hiddenStates, topKIndices, topKWeights ml experts := moe.Down.Weight.MulmatID(ctx, hiddenStates, topKIndices) experts = experts.Mul(ctx, topKWeights) + nextStates := experts.View(ctx, 0, experts.Dim(0), experts.Stride(2), experts.Dim(2)) for i := 1; i < opts.numExpertsUsed; i++ { nextStates = nextStates.Add(ctx, experts.View(ctx, i*experts.Stride(1), experts.Dim(0), experts.Stride(2), experts.Dim(2))) diff --git a/model/models/gemma3n/model_text.go b/model/models/gemma3n/model_text.go index ec038a28..3a89afe7 100644 --- a/model/models/gemma3n/model_text.go +++ b/model/models/gemma3n/model_text.go @@ -64,18 +64,18 @@ func (m *TextModel) Forward(ctx ml.Context, batch input.Batch, cache kvcache.Cac cache.(*kvcache.WrapperCache).SetLayerType(layerType) - // inputPerLayer = inputsPerLayer[:, i, :] - inputPerLayer := inputsPerLayer.View(ctx, i*inputsPerLayer.Stride(1), inputsPerLayer.Dim(0), inputsPerLayer.Stride(2), inputsPerLayer.Dim(2)).Contiguous(ctx) + // inputPerLayer = inputsPerLayer[:, i, :].squeeze(1) + inputPerLayer := inputsPerLayer.View(ctx, i*inputsPerLayer.Stride(1), inputsPerLayer.Dim(0), inputsPerLayer.Stride(2), inputsPerLayer.Dim(2)) hiddenStates = layer.Forward(ctx, hiddenStates, inputPerLayer, positions, one, cache, i >= firstSharedKeyValue, ropeBase, float64(m.activationSparsityScale[i]), &m.TextOptions) } // hiddenStates = hiddenStates[:, :, 0] - hiddenStates0 := hiddenStates.View(ctx, 0, hiddenStates.Dim(0), hiddenStates.Stride(1), hiddenStates.Dim(1)) + hiddenStates0 := hiddenStates.Slice(ctx, 2, 0, 1, 1) targetMagnitude = hiddenStates0.Sqr(ctx).Mean(ctx).Sqrt(ctx) targetMagnitude = targetMagnitude.Repeat(ctx, 2, m.altupInputs-1) // hiddenState = hiddenStates[:, :, 1:] - hiddenState = hiddenStates.View(ctx, hiddenStates.Stride(2), hiddenStates.Dim(0), hiddenStates.Stride(1), hiddenStates.Dim(1), hiddenStates.Stride(2), m.altupInputs-1) + hiddenState = hiddenStates.Slice(ctx, 2, 1, hiddenStates.Dim(2), 1) altupUnembdProj := m.AltupUnembd.Forward(ctx, hiddenState) altupUnembdProj = altupUnembdProj.Mul(ctx, targetMagnitude.Div(ctx, altupUnembdProj.Sqr(ctx).Mean(ctx).Sqrt(ctx))) @@ -176,10 +176,10 @@ func (d TextLayer) Forward(ctx ml.Context, hiddenStates, perLayerInput, position active = d.PostPerLayerNorm.Forward(ctx, active, opts.eps) // inactive := predictions[:, :, 1:] - inactive := predictions.View(ctx, predictions.Stride(2), predictions.Dim(0), predictions.Stride(1), predictions.Dim(1), predictions.Stride(2), predictions.Dim(2)-1) + inactive := predictions.Slice(ctx, 2, 1, predictions.Dim(2), 1) active = inactive.Add(ctx, active) - predictions0 := predictions.View(ctx, 0, predictions.Dim(0), predictions.Stride(1), predictions.Dim(1)) + predictions0 := predictions.Slice(ctx, 2, 0, 1, 1) return predictions0.Concat(ctx, active, 2) } @@ -319,7 +319,7 @@ type TextOptions struct { func (o *TextOptions) altupActive(ctx ml.Context, t ml.Tensor) ml.Tensor { // t[:, :, o.altupActiveIndex] - return t.View(ctx, o.altupActiveIndex*t.Stride(2), t.Dim(0), t.Stride(1), t.Dim(1)) + return t.Slice(ctx, 2, o.altupActiveIndex, o.altupActiveIndex+1, 1) } func (o *TextOptions) headDim() int { diff --git a/model/models/gptoss/model.go b/model/models/gptoss/model.go index c10920f1..da08ed96 100644 --- a/model/models/gptoss/model.go +++ b/model/models/gptoss/model.go @@ -121,30 +121,9 @@ func (attn *AttentionBlock) Forward(ctx ml.Context, hiddenStates, positions ml.T var query, key, value ml.Tensor if attn.QKV != nil { qkv := attn.QKV.Forward(ctx, hiddenStates) - - // query = qkv[..., : num_attention_heads * head_dim].reshape(batch_size, num_attention_heads, head_dim) - query = qkv.View(ctx, - 0, - opts.headDim(), qkv.Stride(0)*opts.headDim(), - opts.numHeads, qkv.Stride(1), - batchSize, - ) - - // key = qkv[..., num_attention_heads * head_dim:(num_attention_heads + num_key_value_heads) * head_dim].reshape(batch_size, num_key_value_heads, head_dim) - key = qkv.View(ctx, - qkv.Stride(0)*opts.headDim()*opts.numHeads, - opts.headDim(), qkv.Stride(0)*opts.headDim(), - opts.numKVHeads, qkv.Stride(1), - batchSize, - ) - - // value = qkv[..., (num_attention_heads + num_key_value_heads) * head_dim:].reshape(batch_size, num_key_value_heads, head_dim) - value = qkv.View(ctx, - qkv.Stride(0)*opts.headDim()*(opts.numHeads+opts.numKVHeads), - opts.headDim(), qkv.Stride(0)*opts.headDim(), - opts.numKVHeads, qkv.Stride(1), - batchSize, - ) + qkv = qkv.Reshape(ctx, opts.headDim(), -1, batchSize) + chunks := qkv.ChunkSections(ctx, 1, opts.numHeads, opts.numKVHeads, opts.numKVHeads) + query, key, value = chunks[0], chunks[1], chunks[2] } else { query = attn.Query.Forward(ctx, hiddenStates) query = query.Reshape(ctx, opts.headDim(), opts.numHeads, batchSize) @@ -195,15 +174,8 @@ func (mlp *MLPBlock) Forward(ctx ml.Context, hiddenStates ml.Tensor, opts *Optio var gate, up ml.Tensor if mlp.GateUp != nil { hiddenStates = mlp.GateUp.Forward(ctx, hiddenStates, selectedExperts) - hiddenStates = hiddenStates.Reshape(ctx, 2, hiddenStates.Dim(0)/2, hiddenStates.Dim(1), hiddenStates.Dim(2)) - - dimStride := []int{hiddenStates.Dim(0) / 2, hiddenStates.Stride(1), hiddenStates.Dim(1), hiddenStates.Stride(2), hiddenStates.Dim(2), hiddenStates.Stride(3), hiddenStates.Dim(3)} - - gate = hiddenStates.View(ctx, 0, dimStride...) - gate = gate.Contiguous(ctx, gate.Dim(0)*gate.Dim(1), gate.Dim(2), gate.Dim(3)) - - up = hiddenStates.View(ctx, hiddenStates.Stride(0), dimStride...) - up = up.Contiguous(ctx, up.Dim(0)*up.Dim(1), up.Dim(2), up.Dim(3)) + gate = hiddenStates.Slice(ctx, 0, 0, hiddenStates.Dim(0), 2) + up = hiddenStates.Slice(ctx, 0, 1, hiddenStates.Dim(0), 2) } else { gate = mlp.Gate.Forward(ctx, hiddenStates, selectedExperts) up = mlp.Up.Forward(ctx, hiddenStates, selectedExperts) diff --git a/model/models/llama4/model.go b/model/models/llama4/model.go index 5eeac07c..4a22bc4b 100644 --- a/model/models/llama4/model.go +++ b/model/models/llama4/model.go @@ -105,9 +105,7 @@ func (m *Model) EncodeMultimodal(ctx ml.Context, multimodalData []byte) ([]input for range aspectRatio.Y { for x := range aspectRatio.X { - view := projectedOutputs.View(ctx, projectedOutputs.Stride(1)*offset, - projectedOutputs.Dim(0), projectedOutputs.Stride(1), - patchesPerChunk) + view := projectedOutputs.Slice(ctx, 1, offset, offset+patchesPerChunk, 1) var separator separator if x < aspectRatio.X-1 { separator.x = true // <|tile_x_separator|> @@ -120,9 +118,7 @@ func (m *Model) EncodeMultimodal(ctx ml.Context, multimodalData []byte) ([]input } } - view := projectedOutputs.View(ctx, projectedOutputs.Stride(1)*offset, - projectedOutputs.Dim(0), projectedOutputs.Stride(1), - patchesPerChunk) + view := projectedOutputs.Slice(ctx, 1, offset, offset+patchesPerChunk, 1) multimodal = append(multimodal, input.Multimodal{Tensor: view, Data: &separator{}}) return multimodal, nil diff --git a/model/models/llama4/model_vision.go b/model/models/llama4/model_vision.go index 1aa50aec..ff6b7fcf 100644 --- a/model/models/llama4/model_vision.go +++ b/model/models/llama4/model_vision.go @@ -37,27 +37,23 @@ type VisionAttention struct { func applyVisionRotaryEmbedding(ctx ml.Context, t, cos, sin ml.Tensor) ml.Tensor { width, height, channels, tiles := t.Dim(0), t.Dim(1), t.Dim(2), t.Dim(3) - t = t.Reshape(ctx, 2, t.Dim(0)/2, t.Dim(1)*t.Dim(2)*t.Dim(3)) - // t1 = t[..., 0::2] - t1 := t.View(ctx, 0, 1, t.Stride(1), t.Dim(1), t.Stride(2), t.Dim(2)).Contiguous(ctx) - t1 = t1.Reshape(ctx, width/2, height, channels, tiles) + t1 := t.Slice(ctx, 0, 0, t.Dim(0), 2) // t2 = t[..., 1::2] - t2 := t.View(ctx, t.Stride(0), 1, t.Stride(1), t.Dim(1), t.Stride(2), t.Dim(2)).Contiguous(ctx) - t2 = t2.Reshape(ctx, width/2, height, channels, tiles) + t2 := t.Slice(ctx, 0, 1, t.Dim(0), 2) // cos_out = torch.stack((t1 * cos, t2 * cos), dim=-1) cosOut := t1.Mul(ctx, cos).Concat(ctx, t2.Mul(ctx, cos), 0) - cosOut = cosOut.Reshape(ctx, cosOut.Dim(0)/2, 2, cosOut.Dim(1)*cosOut.Dim(2)*cosOut.Dim(3)) - cosOut = cosOut.Permute(ctx, 1, 0, 2, 3).Contiguous(ctx) - cosOut = cosOut.Reshape(ctx, width, height, channels, tiles) + cosOut = cosOut.Reshape(ctx, cosOut.Dim(0)/2, 2, -1) + cosOut = cosOut.Permute(ctx, 1, 0, 2, 3) + cosOut = cosOut.Contiguous(ctx, width, height, channels, tiles) // sin_out = torch.stack((-t2 * sin, t1 * sin), dim=-1) - sinOut := t2.Neg(ctx).Mul(ctx, sin).Concat(ctx, t1.Mul(ctx, sin), 0) - sinOut = sinOut.Reshape(ctx, sinOut.Dim(0)/2, 2, sinOut.Dim(1)*sinOut.Dim(2)*sinOut.Dim(3)) - sinOut = sinOut.Permute(ctx, 1, 0, 2, 3).Contiguous(ctx) - sinOut = sinOut.Reshape(ctx, width, height, channels, tiles) + sinOut := t2.Scale(ctx, -1).Mul(ctx, sin).Concat(ctx, t1.Mul(ctx, sin), 0) + sinOut = sinOut.Reshape(ctx, sinOut.Dim(0)/2, 2, -1) + sinOut = sinOut.Permute(ctx, 1, 0, 2, 3) + sinOut = sinOut.Contiguous(ctx, width, height, channels, tiles) return cosOut.Add(ctx, sinOut) } diff --git a/model/models/mistral3/model_vision.go b/model/models/mistral3/model_vision.go index ce3110c7..d763df7a 100644 --- a/model/models/mistral3/model_vision.go +++ b/model/models/mistral3/model_vision.go @@ -11,9 +11,9 @@ import ( var batchSize int = 1 func rotateHalf(ctx ml.Context, t ml.Tensor) ml.Tensor { - x1 := t.View(ctx, 0, t.Dim(0)/2, t.Stride(1), t.Dim(1), t.Stride(2), t.Dim(2), t.Stride(3), t.Dim(3)) - x2 := t.View(ctx, t.Stride(0)*t.Dim(0)/2, t.Dim(0)/2, t.Stride(1), t.Dim(1), t.Stride(2), t.Dim(2), t.Stride(3), t.Dim(3)).Contiguous(ctx) - return x2.Neg(ctx).Concat(ctx, x1, 0) + x1 := t.Slice(ctx, 0, 0, t.Dim(0)/2, 1) + x2 := t.Slice(ctx, 0, t.Dim(0)/2, t.Dim(0), 1).Contiguous(ctx) + return x2.Scale(ctx, -1).Concat(ctx, x1, 0) } func applyRotaryPositionalEmbedding(ctx ml.Context, t, cos, sin ml.Tensor) ml.Tensor { diff --git a/model/models/qwen25vl/model_vision.go b/model/models/qwen25vl/model_vision.go index 88b2c005..5cbb01f7 100644 --- a/model/models/qwen25vl/model_vision.go +++ b/model/models/qwen25vl/model_vision.go @@ -13,9 +13,9 @@ import ( var batchSize int = 1 func rotateHalf(ctx ml.Context, t ml.Tensor) ml.Tensor { - x1 := t.View(ctx, 0, t.Dim(0)/2, t.Stride(1), t.Dim(1), t.Stride(2), t.Dim(2), t.Stride(3), t.Dim(3)) - x2 := t.View(ctx, t.Stride(0)*t.Dim(0)/2, t.Dim(0)/2, t.Stride(1), t.Dim(1), t.Stride(2), t.Dim(2), t.Stride(3), t.Dim(3)).Contiguous(ctx) - return x2.Neg(ctx).Concat(ctx, x1, 0) + x1 := t.Slice(ctx, 0, 0, t.Dim(0)/2, 1) + x2 := t.Slice(ctx, 0, t.Dim(0)/2, t.Dim(0), 1).Contiguous(ctx) + return x2.Scale(ctx, -1).Concat(ctx, x1, 0) } func applyRotaryPositionalEmbedding(ctx ml.Context, t, cos, sin ml.Tensor) ml.Tensor { diff --git a/model/models/qwen3vl/model_vision.go b/model/models/qwen3vl/model_vision.go index 69118666..b22ac305 100644 --- a/model/models/qwen3vl/model_vision.go +++ b/model/models/qwen3vl/model_vision.go @@ -18,8 +18,8 @@ type VisionAttention struct { } func rotateHalf(ctx ml.Context, t ml.Tensor) ml.Tensor { - x1 := t.View(ctx, 0, t.Dim(0)/2, t.Stride(1), t.Dim(1), t.Stride(2), t.Dim(2), t.Stride(3), t.Dim(3)) - x2 := t.View(ctx, t.Stride(0)*t.Dim(0)/2, t.Dim(0)/2, t.Stride(1), t.Dim(1), t.Stride(2), t.Dim(2), t.Stride(3), t.Dim(3)).Contiguous(ctx) + x1 := t.Slice(ctx, 0, 0, t.Dim(0)/2, 1) + x2 := t.Slice(ctx, 0, t.Dim(0)/2, t.Dim(0), 1).Contiguous(ctx) return x2.Scale(ctx, -1).Concat(ctx, x1, 0) } @@ -160,10 +160,11 @@ func (m *VisionPositionEmbedding) Forward(ctx ml.Context, hiddenStates ml.Tensor positionEmbeds = positionEmbeds.Mul(ctx, weights) positionEmbeds = positionEmbeds.Reshape(ctx, n, -1, 4) - positionEmbeds = positionEmbeds.View(ctx, 0, n, positionEmbeds.Stride(1), grid.Height*grid.Width). - Add(ctx, positionEmbeds.View(ctx, 1*positionEmbeds.Stride(2), n, positionEmbeds.Stride(1), grid.Height*grid.Width)). - Add(ctx, positionEmbeds.View(ctx, 2*positionEmbeds.Stride(2), n, positionEmbeds.Stride(1), grid.Height*grid.Width)). - Add(ctx, positionEmbeds.View(ctx, 3*positionEmbeds.Stride(2), n, positionEmbeds.Stride(1), grid.Height*grid.Width)) + positionEmbedsChunks := positionEmbeds.Chunk(ctx, 2, 1) + positionEmbeds = positionEmbedsChunks[0]. + Add(ctx, positionEmbedsChunks[1]). + Add(ctx, positionEmbedsChunks[2]). + Add(ctx, positionEmbedsChunks[3]) positionEmbeds = positionEmbeds.Reshape(ctx, -1, grid.Width/opts.spatialMergeSize, opts.spatialMergeSize, grid.Height/opts.spatialMergeSize) positionEmbeds = positionEmbeds.Permute(ctx, 0, 2, 1, 3).Contiguous(ctx, n, -1) From 12b174b10e5f3d0cf8cf5856a344e1f765203535 Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Thu, 13 Nov 2025 15:32:34 -0800 Subject: [PATCH 32/32] fix tensor merge (#13053) --- convert/tensor.go | 22 +++++++++++++++++++++ convert/tensor_test.go | 44 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/convert/tensor.go b/convert/tensor.go index 27bdd13f..68870744 100644 --- a/convert/tensor.go +++ b/convert/tensor.go @@ -2,10 +2,12 @@ package convert import ( "cmp" + "errors" "io" "iter" "path" "slices" + "strconv" "strings" "github.com/pdevine/tensor" @@ -94,6 +96,26 @@ func mergeTensors(unmatched []Tensor, merges ...merge) (out []*ggml.Tensor, _ [] return matched }) + slices.SortStableFunc(matched, func(a, b Tensor) int { + x := strings.Split(a.Name(), ".") + y := strings.Split(b.Name(), ".") + if len(x) != len(y) { + return cmp.Compare(len(x), len(y)) + } + + vals := make([]int, len(x)) + for i := range x { + vals[i] = strings.Compare(x[i], y[i]) + m, err := strconv.ParseInt(x[i], 0, 0) + n, err2 := strconv.ParseInt(y[i], 0, 0) + if errors.Join(err, err2) == nil { + vals[i] = cmp.Compare(m, n) + } + } + + return cmp.Or(vals...) + }) + if len(matched) > 0 { out = append(out, &ggml.Tensor{ Name: merges[i].name, diff --git a/convert/tensor_test.go b/convert/tensor_test.go index c1f58da6..e0dc2350 100644 --- a/convert/tensor_test.go +++ b/convert/tensor_test.go @@ -3,8 +3,10 @@ package convert import ( "bytes" "encoding/binary" + "fmt" "io" "iter" + "math/rand/v2" "slices" "strings" "testing" @@ -951,3 +953,45 @@ func TestMerge(t *testing.T) { } }) } + +func TestMergeOrder(t *testing.T) { + for range 8 { + t.Run("", func(t *testing.T) { + tensors := make([]Tensor, 16) + for i := range tensors { + tensors[i] = &fakeTensor{ + name: fmt.Sprintf("layer.%d.weight", i), + shape: []uint64{1}, + data: []float32{float32(i)}, + } + } + + rand.Shuffle(len(tensors), func(i, j int) { + tensors[i], tensors[j] = tensors[j], tensors[i] + }) + + matched, unmatched := mergeTensors(tensors, merge{"layer.*.weight", "layer.weight"}) + if len(unmatched) != 0 { + t.Error("expected no remaining tensors, got", len(unmatched)) + } + + if len(matched) != 1 { + t.Error("expected 1 merged tensor, got", len(matched)) + } + + var b bytes.Buffer + if _, err := matched[0].WriteTo(&b); err != nil { + t.Fatal(err) + } + + var f32s [16]float32 + if err := binary.Read(&b, binary.LittleEndian, &f32s); err != nil { + t.Fatal(err) + } + + if !slices.IsSorted(f32s[:]) { + t.Errorf("merged tensor data is not in order: %+v", f32s) + } + }) + } +}