Merge remote-tracking branch 'origin/main' into misc_tweaks

This commit is contained in:
Min Idzelis 2025-03-27 23:47:58 +00:00
commit a1ab910213
92 changed files with 2088 additions and 4895 deletions

View File

@ -395,16 +395,16 @@ jobs:
uv sync --extra cpu
- name: Lint with ruff
run: |
uv run ruff check --output-format=github app export
uv run ruff check --output-format=github immich_ml
- name: Check black formatting
run: |
uv run black --check app export
uv run black --check immich_ml
- name: Run mypy type checking
run: |
uv run mypy --strict app/
uv run mypy --strict immich_ml/
- name: Run tests and coverage
run: |
uv run pytest app --cov=app --cov-report term-missing
uv run pytest --cov=immich_ml --cov-report term-missing
github-files-formatting:
name: .github Files Formatting

216
cli/package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "@immich/cli",
"version": "2.2.56",
"version": "2.2.57",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@immich/cli",
"version": "2.2.56",
"version": "2.2.57",
"license": "GNU Affero General Public License version 3",
"dependencies": {
"chokidar": "^4.0.3",
@ -55,7 +55,7 @@
},
"../open-api/typescript-sdk": {
"name": "@immich/sdk",
"version": "1.130.2",
"version": "1.130.3",
"dev": true,
"license": "GNU Affero General Public License version 3",
"dependencies": {
@ -810,9 +810,9 @@
}
},
"node_modules/@eslint/config-helpers": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.1.0.tgz",
"integrity": "sha512-kLrdPDJE1ckPo94kmPPf9Hfd0DU0Jw6oKYrhe+pwSC0iTUInmTa+w6fw8sGgcfkFJGNdWOUeOaDM4quW4a7OkA==",
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.0.tgz",
"integrity": "sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@ -833,9 +833,9 @@
}
},
"node_modules/@eslint/eslintrc": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz",
"integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==",
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
"integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -894,9 +894,9 @@
}
},
"node_modules/@eslint/js": {
"version": "9.22.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.22.0.tgz",
"integrity": "sha512-vLFajx9o8d1/oL2ZkpMYbkLv8nDB6yaIwFNt7nI4+I80U/z03SxmfOMsLbvWr3p7C+Wnoh//aOu2pQW8cS0HCQ==",
"version": "9.23.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz",
"integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==",
"dev": true,
"license": "MIT",
"engines": {
@ -1528,17 +1528,17 @@
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.26.1.tgz",
"integrity": "sha512-2X3mwqsj9Bd3Ciz508ZUtoQQYpOhU/kWoUqIf49H8Z0+Vbh6UF/y0OEYp0Q0axOGzaBGs7QxRwq0knSQ8khQNA==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.27.0.tgz",
"integrity": "sha512-4henw4zkePi5p252c8ncBLzLce52SEUz2Ebj8faDnuUXz2UuHEONYcJ+G0oaCF+bYCWVZtrGzq3FD7YXetmnSA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.26.1",
"@typescript-eslint/type-utils": "8.26.1",
"@typescript-eslint/utils": "8.26.1",
"@typescript-eslint/visitor-keys": "8.26.1",
"@typescript-eslint/scope-manager": "8.27.0",
"@typescript-eslint/type-utils": "8.27.0",
"@typescript-eslint/utils": "8.27.0",
"@typescript-eslint/visitor-keys": "8.27.0",
"graphemer": "^1.4.0",
"ignore": "^5.3.1",
"natural-compare": "^1.4.0",
@ -1558,16 +1558,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.26.1.tgz",
"integrity": "sha512-w6HZUV4NWxqd8BdeFf81t07d7/YV9s7TCWrQQbG5uhuvGUAW+fq1usZ1Hmz9UPNLniFnD8GLSsDpjP0hm1S4lQ==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.27.0.tgz",
"integrity": "sha512-XGwIabPallYipmcOk45DpsBSgLC64A0yvdAkrwEzwZ2viqGqRUJ8eEYoPz0CWnutgAFbNMPdsGGvzjSmcWVlEA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.26.1",
"@typescript-eslint/types": "8.26.1",
"@typescript-eslint/typescript-estree": "8.26.1",
"@typescript-eslint/visitor-keys": "8.26.1",
"@typescript-eslint/scope-manager": "8.27.0",
"@typescript-eslint/types": "8.27.0",
"@typescript-eslint/typescript-estree": "8.27.0",
"@typescript-eslint/visitor-keys": "8.27.0",
"debug": "^4.3.4"
},
"engines": {
@ -1583,14 +1583,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.26.1.tgz",
"integrity": "sha512-6EIvbE5cNER8sqBu6V7+KeMZIC1664d2Yjt+B9EWUXrsyWpxx4lEZrmvxgSKRC6gX+efDL/UY9OpPZ267io3mg==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.27.0.tgz",
"integrity": "sha512-8oI9GwPMQmBryaaxG1tOZdxXVeMDte6NyJA4i7/TWa4fBwgnAXYlIQP+uYOeqAaLJ2JRxlG9CAyL+C+YE9Xknw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.26.1",
"@typescript-eslint/visitor-keys": "8.26.1"
"@typescript-eslint/types": "8.27.0",
"@typescript-eslint/visitor-keys": "8.27.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -1601,14 +1601,14 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.26.1.tgz",
"integrity": "sha512-Kcj/TagJLwoY/5w9JGEFV0dclQdyqw9+VMndxOJKtoFSjfZhLXhYjzsQEeyza03rwHx2vFEGvrJWJBXKleRvZg==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.27.0.tgz",
"integrity": "sha512-wVArTVcz1oJOIEJxui/nRhV0TXzD/zMSOYi/ggCfNq78EIszddXcJb7r4RCp/oBrjt8n9A0BSxRMKxHftpDxDA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/typescript-estree": "8.26.1",
"@typescript-eslint/utils": "8.26.1",
"@typescript-eslint/typescript-estree": "8.27.0",
"@typescript-eslint/utils": "8.27.0",
"debug": "^4.3.4",
"ts-api-utils": "^2.0.1"
},
@ -1625,9 +1625,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.26.1.tgz",
"integrity": "sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.27.0.tgz",
"integrity": "sha512-/6cp9yL72yUHAYq9g6DsAU+vVfvQmd1a8KyA81uvfDE21O2DwQ/qxlM4AR8TSdAu+kJLBDrEHKC5/W2/nxsY0A==",
"dev": true,
"license": "MIT",
"engines": {
@ -1639,14 +1639,14 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.26.1.tgz",
"integrity": "sha512-yUwPpUHDgdrv1QJ7YQal3cMVBGWfnuCdKbXw1yyjArax3353rEJP1ZA+4F8nOlQ3RfS2hUN/wze3nlY+ZOhvoA==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.27.0.tgz",
"integrity": "sha512-BnKq8cqPVoMw71O38a1tEb6iebEgGA80icSxW7g+kndx0o6ot6696HjG7NdgfuAVmVEtwXUr3L8R9ZuVjoQL6A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.26.1",
"@typescript-eslint/visitor-keys": "8.26.1",
"@typescript-eslint/types": "8.27.0",
"@typescript-eslint/visitor-keys": "8.27.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
@ -1666,16 +1666,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.1.tgz",
"integrity": "sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.27.0.tgz",
"integrity": "sha512-njkodcwH1yvmo31YWgRHNb/x1Xhhq4/m81PhtvmRngD8iHPehxffz1SNCO+kwaePhATC+kOa/ggmvPoPza5i0Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@typescript-eslint/scope-manager": "8.26.1",
"@typescript-eslint/types": "8.26.1",
"@typescript-eslint/typescript-estree": "8.26.1"
"@typescript-eslint/scope-manager": "8.27.0",
"@typescript-eslint/types": "8.27.0",
"@typescript-eslint/typescript-estree": "8.27.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -1690,13 +1690,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.1.tgz",
"integrity": "sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.27.0.tgz",
"integrity": "sha512-WsXQwMkILJvffP6z4U3FYJPlbf/j07HIxmDjZpbNvBJkMfvwXj5ACRkkHwBDvLBbDbtX5TdU64/rcvKJ/vuInQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.26.1",
"@typescript-eslint/types": "8.27.0",
"eslint-visitor-keys": "^4.2.0"
},
"engines": {
@ -1721,9 +1721,9 @@
}
},
"node_modules/@vitest/coverage-v8": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.8.tgz",
"integrity": "sha512-y7SAKsQirsEJ2F8bulBck4DoluhI2EEgTimHd6EEUgJBGKy9tC25cpywh1MH4FvDGoG2Unt7+asVd1kj4qOSAw==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.9.tgz",
"integrity": "sha512-15OACZcBtQ34keIEn19JYTVuMFTlFrClclwWjHo/IRPg/8ELpkgNTl0o7WLP9WO9XGH6+tip9CPYtEOrIDJvBA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -1744,8 +1744,8 @@
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"@vitest/browser": "3.0.8",
"vitest": "3.0.8"
"@vitest/browser": "3.0.9",
"vitest": "3.0.9"
},
"peerDependenciesMeta": {
"@vitest/browser": {
@ -1754,14 +1754,14 @@
}
},
"node_modules/@vitest/expect": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.8.tgz",
"integrity": "sha512-Xu6TTIavTvSSS6LZaA3EebWFr6tsoXPetOWNMOlc7LO88QVVBwq2oQWBoDiLCN6YTvNYsGSjqOO8CAdjom5DCQ==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.9.tgz",
"integrity": "sha512-5eCqRItYgIML7NNVgJj6TVCmdzE7ZVgJhruW0ziSQV4V7PvLkDL1bBkBdcTs/VuIz0IxPb5da1IDSqc1TR9eig==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/spy": "3.0.8",
"@vitest/utils": "3.0.8",
"@vitest/spy": "3.0.9",
"@vitest/utils": "3.0.9",
"chai": "^5.2.0",
"tinyrainbow": "^2.0.0"
},
@ -1770,13 +1770,13 @@
}
},
"node_modules/@vitest/mocker": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.8.tgz",
"integrity": "sha512-n3LjS7fcW1BCoF+zWZxG7/5XvuYH+lsFg+BDwwAz0arIwHQJFUEsKBQ0BLU49fCxuM/2HSeBPHQD8WjgrxMfow==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.9.tgz",
"integrity": "sha512-ryERPIBOnvevAkTq+L1lD+DTFBRcjueL9lOUfXsLfwP92h4e+Heb+PjiqS3/OURWPtywfafK0kj++yDFjWUmrA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/spy": "3.0.8",
"@vitest/spy": "3.0.9",
"estree-walker": "^3.0.3",
"magic-string": "^0.30.17"
},
@ -1797,9 +1797,9 @@
}
},
"node_modules/@vitest/pretty-format": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.8.tgz",
"integrity": "sha512-BNqwbEyitFhzYMYHUVbIvepOyeQOSFA/NeJMIP9enMntkkxLgOcgABH6fjyXG85ipTgvero6noreavGIqfJcIg==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.9.tgz",
"integrity": "sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -1810,13 +1810,13 @@
}
},
"node_modules/@vitest/runner": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.8.tgz",
"integrity": "sha512-c7UUw6gEcOzI8fih+uaAXS5DwjlBaCJUo7KJ4VvJcjL95+DSR1kova2hFuRt3w41KZEFcOEiq098KkyrjXeM5w==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.9.tgz",
"integrity": "sha512-NX9oUXgF9HPfJSwl8tUZCMP1oGx2+Sf+ru6d05QjzQz4OwWg0psEzwY6VexP2tTHWdOkhKHUIZH+fS6nA7jfOw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/utils": "3.0.8",
"@vitest/utils": "3.0.9",
"pathe": "^2.0.3"
},
"funding": {
@ -1824,13 +1824,13 @@
}
},
"node_modules/@vitest/snapshot": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.8.tgz",
"integrity": "sha512-x8IlMGSEMugakInj44nUrLSILh/zy1f2/BgH0UeHpNyOocG18M9CWVIFBaXPt8TrqVZWmcPjwfG/ht5tnpba8A==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.9.tgz",
"integrity": "sha512-AiLUiuZ0FuA+/8i19mTYd+re5jqjEc2jZbgJ2up0VY0Ddyyxg/uUtBDpIFAy4uzKaQxOW8gMgBdAJJ2ydhu39A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "3.0.8",
"@vitest/pretty-format": "3.0.9",
"magic-string": "^0.30.17",
"pathe": "^2.0.3"
},
@ -1839,9 +1839,9 @@
}
},
"node_modules/@vitest/spy": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.8.tgz",
"integrity": "sha512-MR+PzJa+22vFKYb934CejhR4BeRpMSoxkvNoDit68GQxRLSf11aT6CTj3XaqUU9rxgWJFnqicN/wxw6yBRkI1Q==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.9.tgz",
"integrity": "sha512-/CcK2UDl0aQ2wtkp3YVWldrpLRNCfVcIOFGlVGKO4R5eajsH393Z1yiXLVQ7vWsj26JOEjeZI0x5sm5P4OGUNQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -1852,13 +1852,13 @@
}
},
"node_modules/@vitest/utils": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.8.tgz",
"integrity": "sha512-nkBC3aEhfX2PdtQI/QwAWp8qZWwzASsU4Npbcd5RdMPBSSLCpkZp52P3xku3s3uA0HIEhGvEcF8rNkBsz9dQ4Q==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.9.tgz",
"integrity": "sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "3.0.8",
"@vitest/pretty-format": "3.0.9",
"loupe": "^3.1.3",
"tinyrainbow": "^2.0.0"
},
@ -2379,19 +2379,19 @@
}
},
"node_modules/eslint": {
"version": "9.22.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.22.0.tgz",
"integrity": "sha512-9V/QURhsRN40xuHXWjV64yvrzMjcz7ZyNoF2jJFmy9j/SLk0u1OLSZgXi28MrXjymnjEGSR80WCdab3RGMDveQ==",
"version": "9.23.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz",
"integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.19.2",
"@eslint/config-helpers": "^0.1.0",
"@eslint/config-helpers": "^0.2.0",
"@eslint/core": "^0.12.0",
"@eslint/eslintrc": "^3.3.0",
"@eslint/js": "9.22.0",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.23.0",
"@eslint/plugin-kit": "^0.2.7",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
@ -4432,9 +4432,9 @@
}
},
"node_modules/vite-node": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.8.tgz",
"integrity": "sha512-6PhR4H9VGlcwXZ+KWCdMqbtG649xCPZqfI9j2PsK1FcXgEzro5bGHcVKFCTqPLaNKZES8Evqv4LwvZARsq5qlg==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.9.tgz",
"integrity": "sha512-w3Gdx7jDcuT9cNn9jExXgOyKmf5UOTb6WMHz8LGAm54eS1Elf5OuBhCxl6zJxGhEeIkgsE1WbHuoL0mj/UXqXg==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -4475,19 +4475,19 @@
}
},
"node_modules/vitest": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.8.tgz",
"integrity": "sha512-dfqAsNqRGUc8hB9OVR2P0w8PZPEckti2+5rdZip0WIz9WW0MnImJ8XiR61QhqLa92EQzKP2uPkzenKOAHyEIbA==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.9.tgz",
"integrity": "sha512-BbcFDqNyBlfSpATmTtXOAOj71RNKDDvjBM/uPfnxxVGrG+FSH2RQIwgeEngTaTkuU/h0ScFvf+tRcKfYXzBybQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/expect": "3.0.8",
"@vitest/mocker": "3.0.8",
"@vitest/pretty-format": "^3.0.8",
"@vitest/runner": "3.0.8",
"@vitest/snapshot": "3.0.8",
"@vitest/spy": "3.0.8",
"@vitest/utils": "3.0.8",
"@vitest/expect": "3.0.9",
"@vitest/mocker": "3.0.9",
"@vitest/pretty-format": "^3.0.9",
"@vitest/runner": "3.0.9",
"@vitest/snapshot": "3.0.9",
"@vitest/spy": "3.0.9",
"@vitest/utils": "3.0.9",
"chai": "^5.2.0",
"debug": "^4.4.0",
"expect-type": "^1.1.0",
@ -4499,7 +4499,7 @@
"tinypool": "^1.0.2",
"tinyrainbow": "^2.0.0",
"vite": "^5.0.0 || ^6.0.0",
"vite-node": "3.0.8",
"vite-node": "3.0.9",
"why-is-node-running": "^2.3.0"
},
"bin": {
@ -4515,8 +4515,8 @@
"@edge-runtime/vm": "*",
"@types/debug": "^4.1.12",
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
"@vitest/browser": "3.0.8",
"@vitest/ui": "3.0.8",
"@vitest/browser": "3.0.9",
"@vitest/ui": "3.0.9",
"happy-dom": "*",
"jsdom": "*"
},

View File

@ -1,6 +1,6 @@
{
"name": "@immich/cli",
"version": "2.2.56",
"version": "2.2.57",
"description": "Command Line Interface (CLI) for Immich",
"type": "module",
"exports": "./dist/index.js",

View File

@ -71,7 +71,7 @@ You do not need to redo any machine learning jobs after enabling hardware accele
1. If you do not already have it, download the latest [`hwaccel.ml.yml`][hw-file] file and ensure it's in the same folder as the `docker-compose.yml`.
2. In the `docker-compose.yml` under `immich-machine-learning`, uncomment the `extends` section and change `cpu` to the appropriate backend.
3. Still in `immich-machine-learning`, add one of -[armnn, cuda, rocm, openvino] to the `image` section's tag at the end of the line.
3. Still in `immich-machine-learning`, add one of -[armnn, cuda, rocm, openvino, rknn] to the `image` section's tag at the end of the line.
4. Redeploy the `immich-machine-learning` container with these updated settings.
### Confirming Device Usage

View File

@ -81,7 +81,7 @@ Memory and execution time estimates were obtained without acceleration on a 7800
**Memory (MiB)**: The peak RSS usage of the process afer performing the above timing benchmark. Does not include image decoding, concurrent processing, the web server, etc., which are relatively constant factors.
**Recall (%)**: Evaluated on Crossmodal-3600, the average of the recall@1, recall@5 and recall@10 results for zeroshot image retrieval.
**Recall (%)**: Evaluated on Crossmodal-3600, the average of the recall@1, recall@5 and recall@10 results for zeroshot image retrieval. Chinese (Simplified), English, French, German, Italian, Japanese, Korean, Polish, Russian, Spanish and Turkish are additionally tested on XTD-10. Chinese (Simplified) and English are additionally tested on Flickr30k. The recall metrics are the average across all tested datasets.
**Pareto Optimal**: Whether the model is not completely outclassed by another model. Try to use models that are optimal for the languages relevant to you. Specifically, for a given model and language, if there's another model that's better for that language in at least one respect (memory usage, execution time, recall) while being at least as good for that language in every other way, then the model is not optimal for that language.
@ -93,59 +93,59 @@ Memory and execution time estimates were obtained without acceleration on a 7800
<summary>English</summary>
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 75.73 | ✅ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 75.44 | ✅ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 75.19 | ✅ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 75.09 | ❌ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 75.07 | ❌ |
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 75.01 | ❌ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 74.92 | ❌ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 74.9 | ❌ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 74.87 | ❌ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 74.87 | ❌ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 74.77 | ❌ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 74.28 | ❌ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 74.26 | ✅ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 73.15 | ✅ |
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 72.78 | ✅ |
| ViT-SO400M-14-SigLIP-384__webli | 4417 | 72.19 | 72.58 | ❌ |
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 72.57 | ❌ |
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 72.47 | ✅ |
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 72.45 | ✅ |
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 72.44 | ❌ |
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 72.37 | ❌ |
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 71.64 | ✅ |
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 71.63 | ❌ |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 71.45 | ❌ |
| ViT-H-14__laion2b-s32b-b79k | 4676 | 39.06 | 71.33 | ❌ |
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 71.19 | ❌ |
| ViT-L-14__laion2b-s32b-b82k | 2233 | 20.56 | 69.86 | ❌ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 69.66 | ❌ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 69.38 | ❌ |
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 68.78 | ✅ |
| ViT-L-14__laion400m_e31 | 2183 | 19.87 | 68.53 | ❌ |
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 68.53 | ❌ |
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 68.53 | ❌ |
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 68.51 | ❌ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 68.41 | ❌ |
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 68.41 | ❌ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 68.33 | ❌ |
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 66.96 | ✅ |
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 66.95 | ✅ |
| ViT-B-32__laion400m_e31 | 999 | 2.28 | 65.65 | ✅ |
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 65.49 | ❌ |
| ViT-L-14__openai | 2212 | 19.91 | 60.12 | ❌ |
| ViT-B-32__openai | 1004 | 2.26 | 59.37 | ✅ |
| RN50x64__openai | 5079 | 48.79 | 59.36 | ❌ |
| RN50x16__openai | 2221 | 15.87 | 59.17 | ❌ |
| ViT-L-14-336__openai | 2616 | 43.45 | 59.09 | ❌ |
| RN50__openai | 913 | 2.39 | 58.32 | ✅ |
| ViT-B-16__openai | 985 | 5.03 | 58.27 | ❌ |
| RN50x4__openai | 1416 | 5.85 | 57.88 | ❌ |
| RN50__cc12m | 914 | 2.37 | 57.75 | ✅ |
| RN101__openai | 1111 | 3.21 | 57.7 | ❌ |
| RN101__yfcc15m | 1111 | 3.22 | 50.11 | ❌ |
| RN50__yfcc15m | 908 | 2.34 | 48.28 | ✅ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 85.99 | ✅ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 85.96 | ❌ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 85.96 | ❌ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 85.93 | ❌ |
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 85.78 | ❌ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 85.75 | ✅ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 85.62 | ✅ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 85.53 | ✅ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 85.48 | ❌ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 85.47 | ✅ |
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 85.09 | ❌ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 85.03 | ✅ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 84.86 | ✅ |
| ViT-SO400M-14-SigLIP-384__webli | 4417 | 72.19 | 84.61 | ❌ |
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 84.17 | ❌ |
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 83.51 | ❌ |
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 83.28 | ✅ |
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 83.24 | ❌ |
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 83.23 | ❌ |
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 83.19 | ✅ |
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 82.54 | ❌ |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 82.43 | ❌ |
| ViT-H-14__laion2b-s32b-b79k | 4676 | 39.06 | 82.36 | ❌ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 82.28 | ✅ |
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 81.9 | ✅ |
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 81.9 | ❌ |
| ViT-L-14__laion2b-s32b-b82k | 2233 | 20.56 | 80.82 | ❌ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 80.65 | ❌ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 80.16 | ❌ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 79.78 | ❌ |
| ViT-L-14__laion400m_e31 | 2183 | 19.87 | 78.64 | ❌ |
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 78.6 | ❌ |
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 78.06 | ❌ |
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 78.06 | ❌ |
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 77.62 | ✅ |
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 77.47 | ❌ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 76.91 | ❌ |
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 76.43 | ✅ |
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 76.35 | ❌ |
| ViT-B-32__laion400m_e31 | 999 | 2.28 | 73.83 | ✅ |
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 73.62 | ❌ |
| RN50x64__openai | 5079 | 48.79 | 73.34 | ❌ |
| ViT-L-14__openai | 2212 | 19.91 | 72.99 | ❌ |
| ViT-L-14-336__openai | 2616 | 43.45 | 72.76 | ❌ |
| RN50x16__openai | 2221 | 15.87 | 72.59 | ❌ |
| RN50x4__openai | 1416 | 5.85 | 70.8 | ❌ |
| ViT-B-16__openai | 985 | 5.03 | 70.01 | ❌ |
| ViT-B-32__openai | 1004 | 2.26 | 69.9 | ✅ |
| RN101__openai | 1111 | 3.21 | 69.3 | ❌ |
| RN50__openai | 913 | 2.39 | 69.02 | ✅ |
| RN50__cc12m | 914 | 2.37 | 64.59 | ✅ |
| RN101__yfcc15m | 1111 | 3.22 | 55.21 | ❌ |
| RN50__yfcc15m | 908 | 2.34 | 53.63 | ✅ |
</details>
<details>
<summary>Arabic</summary>
@ -156,8 +156,8 @@ Memory and execution time estimates were obtained without acceleration on a 7800
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 74.03 | ✅ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 73.19 | ✅ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 69.31 | ✅ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 69.29 | ❌ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 69.29 | ❌ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 69.29 | ❌ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 68.64 | ✅ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 68.35 | ✅ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 68.25 | ✅ |
@ -195,25 +195,25 @@ Memory and execution time estimates were obtained without acceleration on a 7800
<summary>Chinese (Simplified)</summary>
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 77.49 | ✅ |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 77.19 | ✅ |
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 76.98 | ❌ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 72.89 | ✅ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 72.65 | ✅ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 72.52 | ✅ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 67.83 | ❌ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 67.81 | ❌ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 67.51 | ❌ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 67.39 | ❌ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 67.33 | ❌ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 67.23 | ❌ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 67.05 | ❌ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 66.87 | ✅ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 66.24 | ❌ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 66.1 | ✅ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 65.56 | ❌ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 64.39 | ❌ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 62.56 | ❌ |
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 79.7 | ✅ |
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 78.94 | ❌ |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 75.22 | ✅ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 74.8 | ✅ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 73.91 | ❌ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 72.8 | ❌ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 72.77 | ❌ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 72.41 | ✅ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 72.36 | ✅ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 71.59 | ❌ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 71.37 | ✅ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 71.3 | ✅ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 71.11 | ✅ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 70.95 | ✅ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 70.51 | ✅ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 67.48 | ✅ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 66.84 | ✅ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 65.7 | ✅ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 63.38 | ❌ |
</details>
<details>
<summary>Croatian</summary>
@ -324,8 +324,8 @@ Memory and execution time estimates were obtained without acceleration on a 7800
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 80.05 | ✅ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 79.81 | ❌ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 79.72 | ❌ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 79.72 | ✅ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 79.72 | ❌ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 79.64 | ✅ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 79.49 | ✅ |
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 79.41 | ❌ |
@ -357,8 +357,8 @@ Memory and execution time estimates were obtained without acceleration on a 7800
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 29.56 | ❌ |
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 29.54 | ✅ |
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 29.36 | ❌ |
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 27.76 | ❌ |
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 27.76 | ❌ |
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 27.76 | ❌ |
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 25.67 | ✅ |
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 25.59 | ❌ |
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 25.53 | ❌ |
@ -384,8 +384,8 @@ Memory and execution time estimates were obtained without acceleration on a 7800
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 34.27 | ❌ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 34.14 | ❌ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 33.98 | ❌ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 30.57 | ❌ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 30.57 | ❌ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 30.57 | ❌ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 30.05 | ✅ |
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 24.92 | ❌ |
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 24.02 | ❌ |
@ -422,110 +422,111 @@ Memory and execution time estimates were obtained without acceleration on a 7800
<summary>French</summary>
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 88.01 | ✅ |
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 87.74 | ❌ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 87.69 | ✅ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 87.6 | ✅ |
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 87.58 | ✅ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 87.51 | ✅ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 87.23 | ❌ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 86.9 | ✅ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 86.9 | ✅ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 86.44 | ✅ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 86.44 | ❌ |
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 86.28 | ❌ |
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 86.11 | ❌ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 86.08 | ✅ |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 84.49 | ❌ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 84.3 | ✅ |
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 83.03 | ✅ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 82.93 | ❌ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 82.27 | ✅ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 82.14 | ❌ |
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 80.96 | ❌ |
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 80.64 | ❌ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 80.28 | ✅ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 79.65 | ✅ |
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 77.4 | ✅ |
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 76.88 | ✅ |
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 76.3 | ✅ |
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 75.68 | ❌ |
| ViT-SO400M-14-SigLIP-384__webli | 4417 | 72.19 | 69.59 | ❌ |
| ViT-H-14__laion2b-s32b-b79k | 4676 | 39.06 | 68.36 | ❌ |
| ViT-L-14__laion2b-s32b-b82k | 2233 | 20.56 | 61.78 | ❌ |
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 58.4 | ❌ |
| ViT-L-14__laion400m_e31 | 2183 | 19.87 | 58.35 | ❌ |
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 57.17 | ❌ |
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 57.17 | ❌ |
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 57.05 | ✅ |
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 56.08 | ✅ |
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 52.96 | ✅ |
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 52.83 | ✅ |
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 51.88 | ❌ |
| ViT-B-32__laion400m_e31 | 999 | 2.28 | 51.82 | ✅ |
| RN50x64__openai | 5079 | 48.79 | 42.86 | ❌ |
| ViT-L-14-336__openai | 2616 | 43.45 | 42.81 | ❌ |
| ViT-L-14__openai | 2212 | 19.91 | 42.54 | ❌ |
| RN50x16__openai | 2221 | 15.87 | 41.72 | ❌ |
| RN50x4__openai | 1416 | 5.85 | 38.85 | ❌ |
| RN101__openai | 1111 | 3.21 | 36.79 | ❌ |
| ViT-B-16__openai | 985 | 5.03 | 36.47 | ❌ |
| ViT-B-32__openai | 1004 | 2.26 | 35.17 | ✅ |
| RN50__openai | 913 | 2.39 | 34.44 | ✅ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 86.5 | ✅ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 86.5 | ❌ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 86.39 | ❌ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 86.15 | ❌ |
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 86.1 | ❌ |
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 86.07 | ❌ |
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 86.06 | ❌ |
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 85.89 | ✅ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 85.67 | ✅ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 85.67 | ✅ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 85.63 | ❌ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 85.39 | ✅ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 85.35 | ✅ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 84.97 | ✅ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 83.8 | ✅ |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 82.96 | ❌ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 82.91 | ✅ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 82.52 | ❌ |
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 81.21 | ✅ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 80.23 | ✅ |
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 79.85 | ❌ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 79.47 | ✅ |
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 79.3 | ❌ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 77.49 | ✅ |
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 76.82 | ✅ |
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 75.94 | ✅ |
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 75.3 | ✅ |
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 75.24 | ❌ |
| ViT-H-14__laion2b-s32b-b79k | 4676 | 39.06 | 69.33 | ❌ |
| ViT-SO400M-14-SigLIP-384__webli | 4417 | 72.19 | 64.41 | ❌ |
| ViT-L-14__laion2b-s32b-b82k | 2233 | 20.56 | 62.86 | ❌ |
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 59.27 | ❌ |
| ViT-L-14__laion400m_e31 | 2183 | 19.87 | 59.09 | ❌ |
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 58.25 | ❌ |
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 58.25 | ❌ |
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 56.97 | ✅ |
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 56.21 | ✅ |
| ViT-B-32__laion400m_e31 | 999 | 2.28 | 53.36 | ✅ |
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 53.33 | ✅ |
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 53.26 | ❌ |
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 53.22 | ❌ |
| ViT-L-14__openai | 2212 | 19.91 | 46.34 | ❌ |
| RN50x64__openai | 5079 | 48.79 | 46.3 | ❌ |
| ViT-L-14-336__openai | 2616 | 43.45 | 45.95 | ❌ |
| RN50x16__openai | 2221 | 15.87 | 45.69 | ❌ |
| RN50x4__openai | 1416 | 5.85 | 42.48 | ❌ |
| RN101__openai | 1111 | 3.21 | 40.16 | ❌ |
| ViT-B-16__openai | 985 | 5.03 | 40.1 | ❌ |
| ViT-B-32__openai | 1004 | 2.26 | 38.27 | ✅ |
| RN50__openai | 913 | 2.39 | 37.8 | ✅ |
</details>
<details>
<summary>German</summary>
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 90.04 | ✅ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 89.97 | ✅ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 89.85 | ❌ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 89.81 | ✅ |
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 89.77 | ❌ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 89.69 | ✅ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 89.45 | ✅ |
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 89.44 | ❌ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 89.39 | ✅ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 89.35 | ✅ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 89.03 | ❌ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 88.82 | ✅ |
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 88.55 | ❌ |
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 88.42 | ❌ |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 87.19 | ❌ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 86.44 | ✅ |
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 84.81 | ✅ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 84.81 | ❌ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 84.58 | ❌ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 84.44 | ✅ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 83.33 | ✅ |
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 82.75 | ❌ |
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 82.32 | ❌ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 81.63 | ✅ |
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 76.76 | ✅ |
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 76.33 | ✅ |
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 75.19 | ✅ |
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 75.07 | ❌ |
| ViT-SO400M-14-SigLIP-384__webli | 4417 | 72.19 | 64.61 | ❌ |
| ViT-H-14__laion2b-s32b-b79k | 4676 | 39.06 | 52.81 | ❌ |
| ViT-L-14__laion2b-s32b-b82k | 2233 | 20.56 | 42.88 | ❌ |
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 38.65 | ❌ |
| ViT-L-14__laion400m_e31 | 2183 | 19.87 | 38.37 | ❌ |
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 37.65 | ✅ |
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 36.6 | ✅ |
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 35.44 | ❌ |
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 35.44 | ❌ |
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 32.46 | ✅ |
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 32.31 | ✅ |
| ViT-B-32__laion400m_e31 | 999 | 2.28 | 31.85 | ✅ |
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 31.81 | ❌ |
| RN50x64__openai | 5079 | 48.79 | 28.41 | ❌ |
| ViT-L-14__openai | 2212 | 19.91 | 27.63 | ❌ |
| ViT-L-14-336__openai | 2616 | 43.45 | 27.09 | ❌ |
| RN50x16__openai | 2221 | 15.87 | 24.48 | ❌ |
| RN50x4__openai | 1416 | 5.85 | 23.49 | ❌ |
| RN50__openai | 913 | 2.39 | 20.91 | ✅ |
| ViT-B-16__openai | 985 | 5.03 | 20.83 | ❌ |
| RN101__openai | 1111 | 3.21 | 20.39 | ❌ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 87.32 | ✅ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 87.29 | ❌ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 87.29 | ❌ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 87.21 | ✅ |
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 87.18 | ❌ |
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 87.14 | ❌ |
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 87.07 | ❌ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 86.83 | ❌ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 86.81 | ✅ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 86.75 | ✅ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 86.74 | ✅ |
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 86.68 | ❌ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 86.56 | ✅ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 86.16 | ✅ |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 84.54 | ❌ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 84.41 | ✅ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 84.25 | ✅ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 83.8 | ❌ |
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 82.59 | ✅ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 81.53 | ✅ |
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 81.34 | ❌ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 81.15 | ✅ |
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 81.05 | ❌ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 78.35 | ✅ |
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 76.56 | ✅ |
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 76.0 | ✅ |
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 75.21 | ✅ |
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 75.14 | ❌ |
| ViT-SO400M-14-SigLIP-384__webli | 4417 | 72.19 | 65.86 | ❌ |
| ViT-H-14__laion2b-s32b-b79k | 4676 | 39.06 | 56.87 | ❌ |
| ViT-L-14__laion2b-s32b-b82k | 2233 | 20.56 | 47.19 | ❌ |
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 43.36 | ❌ |
| ViT-L-14__laion400m_e31 | 2183 | 19.87 | 43.0 | ❌ |
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 41.81 | ✅ |
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 40.43 | ✅ |
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 40.41 | ❌ |
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 40.41 | ❌ |
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 37.71 | ✅ |
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 37.64 | ✅ |
| ViT-B-32__laion400m_e31 | 999 | 2.28 | 36.04 | ✅ |
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 35.9 | ❌ |
| RN50x64__openai | 5079 | 48.79 | 34.19 | ❌ |
| ViT-L-14__openai | 2212 | 19.91 | 33.1 | ❌ |
| ViT-L-14-336__openai | 2616 | 43.45 | 32.25 | ❌ |
| RN50x16__openai | 2221 | 15.87 | 30.56 | ❌ |
| RN50x4__openai | 1416 | 5.85 | 29.2 | ❌ |
| ViT-B-16__openai | 985 | 5.03 | 25.77 | ❌ |
| RN101__openai | 1111 | 3.21 | 25.46 | ❌ |
| RN50__openai | 913 | 2.39 | 24.92 | ✅ |
| ViT-B-32__openai | 1004 | 2.26 | 24.13 | ✅ |
</details>
<details>
<summary>Greek</summary>
@ -542,10 +543,10 @@ Memory and execution time estimates were obtained without acceleration on a 7800
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 60.63 | ❌ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 60.41 | ❌ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 60.1 | ❌ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 60.06 | ❌ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 60.06 | ❌ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 59.44 | ❌ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 60.06 | ❌ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 59.44 | ❌ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 59.44 | ❌ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 59.43 | ✅ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 58.78 | ✅ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 53.42 | ❌ |
@ -670,99 +671,104 @@ Memory and execution time estimates were obtained without acceleration on a 7800
<summary>Italian</summary>
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 88.6 | ✅ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 88.25 | ✅ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 88.12 | ✅ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 88.04 | ✅ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 87.97 | ❌ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 87.69 | ❌ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 87.29 | ✅ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 87.06 | ❌ |
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 86.91 | ❌ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 86.88 | ✅ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 86.68 | ✅ |
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 86.61 | ❌ |
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 85.55 | ❌ |
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 85.37 | ❌ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 83.78 | ✅ |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 83.0 | ❌ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 81.81 | ✅ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 81.77 | ❌ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 81.32 | ❌ |
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 80.97 | ❌ |
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 80.53 | ✅ |
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 80.1 | ❌ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 79.71 | ✅ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 77.31 | ✅ |
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 75.19 | ✅ |
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 74.49 | ✅ |
| ViT-SO400M-14-SigLIP-384__webli | 4417 | 72.19 | 74.04 | ❌ |
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 73.68 | ✅ |
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 73.57 | ✅ |
| ViT-H-14__laion2b-s32b-b79k | 4676 | 39.06 | 51.04 | ❌ |
| ViT-L-14__laion2b-s32b-b82k | 2233 | 20.56 | 41.73 | ❌ |
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 36.87 | ❌ |
| ViT-L-14__laion400m_e31 | 2183 | 19.87 | 36.84 | ❌ |
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 34.68 | ❌ |
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 34.68 | ❌ |
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 34.64 | ✅ |
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 33.8 | ✅ |
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 30.11 | ✅ |
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 30.04 | ❌ |
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 29.89 | ❌ |
| ViT-B-32__laion400m_e31 | 999 | 2.28 | 29.88 | ✅ |
| RN50x64__openai | 5079 | 48.79 | 26.67 | ❌ |
| ViT-L-14__openai | 2212 | 19.91 | 25.51 | ❌ |
| ViT-L-14-336__openai | 2616 | 43.45 | 25.3 | ❌ |
| RN50x16__openai | 2221 | 15.87 | 21.37 | ❌ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 87.17 | ✅ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 86.91 | ✅ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 86.83 | ❌ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 86.77 | ✅ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 86.67 | ✅ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 86.42 | ❌ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 86.35 | ✅ |
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 86.34 | ❌ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 86.18 | ✅ |
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 86.17 | ❌ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 85.84 | ✅ |
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 85.8 | ❌ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 85.7 | ✅ |
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 85.67 | ❌ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 83.32 | ✅ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 82.95 | ❌ |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 82.73 | ❌ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 82.72 | ❌ |
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 81.07 | ❌ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 80.8 | ✅ |
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 80.6 | ✅ |
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 80.35 | ❌ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 78.79 | ✅ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 76.62 | ✅ |
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 76.51 | ✅ |
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 76.08 | ✅ |
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 75.29 | ✅ |
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 75.29 | ❌ |
| ViT-SO400M-14-SigLIP-384__webli | 4417 | 72.19 | 74.84 | ❌ |
| ViT-H-14__laion2b-s32b-b79k | 4676 | 39.06 | 56.32 | ❌ |
| ViT-L-14__laion2b-s32b-b82k | 2233 | 20.56 | 47.25 | ❌ |
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 43.09 | ❌ |
| ViT-L-14__laion400m_e31 | 2183 | 19.87 | 42.99 | ❌ |
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 40.29 | ❌ |
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 40.29 | ❌ |
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 39.67 | ✅ |
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 39.03 | ✅ |
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 36.14 | ✅ |
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 35.89 | ❌ |
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 35.59 | ❌ |
| ViT-B-32__laion400m_e31 | 999 | 2.28 | 35.56 | ✅ |
| RN50x64__openai | 5079 | 48.79 | 33.53 | ❌ |
| ViT-L-14__openai | 2212 | 19.91 | 32.19 | ❌ |
| ViT-L-14-336__openai | 2616 | 43.45 | 30.95 | ❌ |
| RN50x16__openai | 2221 | 15.87 | 28.85 | ❌ |
| RN50x4__openai | 1416 | 5.85 | 25.75 | ❌ |
| ViT-B-16__openai | 985 | 5.03 | 25.18 | ❌ |
| RN101__openai | 1111 | 3.21 | 24.48 | ❌ |
| RN50__openai | 913 | 2.39 | 23.89 | ✅ |
| ViT-B-32__openai | 1004 | 2.26 | 23.39 | ✅ |
</details>
<details>
<summary>Japanese</summary>
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 86.97 | ✅ |
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 85.15 | ❌ |
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 84.69 | ❌ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 81.77 | ✅ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 81.26 | ❌ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 81.19 | ✅ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 69.99 | ❌ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 68.58 | ❌ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 68.35 | ❌ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 68.29 | ❌ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 67.99 | ❌ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 67.68 | ❌ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 67.67 | ❌ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 66.85 | ✅ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 66.54 | ❌ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 65.77 | ❌ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 61.48 | ✅ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 58.1 | ❌ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 55.31 | ❌ |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 83.95 | ✅ |
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 82.21 | ❌ |
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 81.55 | ❌ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 78.72 | ✅ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 78.53 | ❌ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 75.93 | ✅ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 66.86 | ❌ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 65.59 | ❌ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 65.48 | ❌ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 65.36 | ❌ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 64.47 | ❌ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 64.17 | ❌ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 64.08 | ❌ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 63.69 | ✅ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 63.33 | ❌ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 63.02 | ❌ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 58.39 | ✅ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 56.38 | ❌ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 53.16 | ❌ |
</details>
<details>
<summary>Korean</summary>
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 77.21 | ✅ |
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 76.89 | ✅ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 75.72 | ✅ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 75.06 | ✅ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 74.94 | ❌ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 74.36 | ✅ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 74.09 | ✅ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 73.61 | ❌ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 73.55 | ✅ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 73.41 | ✅ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 73.18 | ✅ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 72.79 | ✅ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 72.27 | ❌ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 71.73 | ✅ |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 71.12 | ❌ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 70.25 | ✅ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 67.54 | ✅ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 67.37 | ✅ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 65.44 | ✅ |
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 80.56 | ✅ |
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 80.53 | ✅ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 77.09 | ✅ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 77.08 | ✅ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 76.97 | ❌ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 76.92 | ✅ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 76.58 | ✅ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 76.2 | ✅ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 75.95 | ✅ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 75.86 | ✅ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 75.67 | ✅ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 75.49 | ❌ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 74.6 | ❌ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 74.52 | ✅ |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 73.88 | ❌ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 71.09 | ✅ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 68.87 | ✅ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 67.94 | ✅ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 66.39 | ✅ |
</details>
<details>
<summary>Maori</summary>
@ -834,34 +840,34 @@ Memory and execution time estimates were obtained without acceleration on a 7800
<summary>Polish</summary>
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 80.6 | ✅ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 80.17 | ✅ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 80.06 | ❌ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 80.04 | ✅ |
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 79.98 | ❌ |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 79.8 | ✅ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 79.72 | ✅ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 79.66 | ❌ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 79.45 | ✅ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 79.26 | ❌ |
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 79.21 | ❌ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 79.14 | ✅ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 78.23 | ✅ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 75.33 | ✅ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 74.7 | ✅ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 74.63 | ❌ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 73.69 | ✅ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 73.44 | ❌ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 70.34 | ❌ |
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 59.4 | ❌ |
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 59.14 | ❌ |
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 48.74 | ❌ |
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 48.35 | ❌ |
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 40.76 | ✅ |
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 39.13 | ✅ |
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 39.09 | ❌ |
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 38.55 | ❌ |
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 38.46 | ❌ |
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 83.49 | ✅ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 83.45 | ❌ |
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 83.11 | ✅ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 82.99 | ✅ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 82.96 | ❌ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 82.93 | ❌ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 82.61 | ❌ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 82.26 | ✅ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 82.24 | ✅ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 82.03 | ✅ |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 82.03 | ❌ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 81.92 | ✅ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 81.27 | ✅ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 80.0 | ✅ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 79.65 | ✅ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 76.75 | ✅ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 76.52 | ✅ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 75.1 | ✅ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 73.9 | ❌ |
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 65.03 | ❌ |
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 64.89 | ❌ |
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 51.6 | ❌ |
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 51.29 | ❌ |
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 46.15 | ✅ |
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 41.55 | ✅ |
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 41.17 | ✅ |
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 40.9 | ✅ |
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 40.76 | ✅ |
</details>
<details>
<summary>Portuguese</summary>
@ -955,84 +961,87 @@ Memory and execution time estimates were obtained without acceleration on a 7800
<summary>Russian</summary>
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 87.65 | ✅ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 87.62 | ❌ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 87.4 | ✅ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 87.39 | ❌ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 86.88 | ❌ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 86.87 | ✅ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 86.74 | ✅ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 86.26 | ✅ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 85.98 | ✅ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 85.66 | ❌ |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 85.54 | ❌ |
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 84.69 | ❌ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 84.29 | ✅ |
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 84.24 | ❌ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 82.86 | ✅ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 81.59 | ✅ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 80.56 | ✅ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 80.44 | ❌ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 79.99 | ❌ |
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 39.51 | ❌ |
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 39.16 | ❌ |
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 23.33 | ❌ |
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 22.4 | ❌ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 84.54 | ✅ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 84.41 | ❌ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 84.36 | ❌ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 84.31 | ❌ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 84.22 | ✅ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 83.9 | ✅ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 83.69 | ✅ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 83.5 | ✅ |
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 83.31 | ❌ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 83.21 | ❌ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 83.11 | ✅ |
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 82.7 | ❌ |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 82.69 | ❌ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 80.91 | ✅ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 79.75 | ❌ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 79.35 | ✅ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 78.91 | ❌ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 78.06 | ✅ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 76.44 | ✅ |
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 42.81 | ❌ |
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 42.1 | ❌ |
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 24.95 | ❌ |
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 24.25 | ❌ |
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 20.85 | ✅ |
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 20.44 | ✅ |
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 20.41 | ❌ |
</details>
<details>
<summary>Spanish</summary>
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 84.24 | ✅ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 83.94 | ✅ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 83.91 | ❌ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 83.78 | ✅ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 83.71 | ✅ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 83.59 | ❌ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 83.2 | ✅ |
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 83.0 | ❌ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 82.91 | ✅ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 82.58 | ❌ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 82.5 | ✅ |
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 82.48 | ❌ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 82.22 | ✅ |
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 81.34 | ❌ |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 80.18 | ❌ |
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 80.14 | ❌ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 78.99 | ✅ |
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 78.19 | ✅ |
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 78.15 | ❌ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 77.93 | ✅ |
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 77.64 | ❌ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 77.21 | ❌ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 76.36 | ❌ |
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 75.73 | ✅ |
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 75.56 | ✅ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 75.01 | ✅ |
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 74.62 | ✅ |
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 74.6 | ✅ |
| ViT-SO400M-14-SigLIP-384__webli | 4417 | 72.19 | 70.31 | ❌ |
| ViT-H-14__laion2b-s32b-b79k | 4676 | 39.06 | 58.31 | ❌ |
| ViT-L-14__laion2b-s32b-b82k | 2233 | 20.56 | 49.56 | ❌ |
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 46.69 | ❌ |
| ViT-L-14__laion400m_e31 | 2183 | 19.87 | 46.53 | ❌ |
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 44.05 | ❌ |
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 44.05 | ❌ |
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 43.67 | ✅ |
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 42.5 | ✅ |
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 41.03 | ✅ |
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 40.91 | ❌ |
| ViT-B-32__laion400m_e31 | 999 | 2.28 | 40.3 | ✅ |
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 40.3 | ❌ |
| RN50x64__openai | 5079 | 48.79 | 37.92 | ❌ |
| ViT-L-14-336__openai | 2616 | 43.45 | 37.7 | ❌ |
| ViT-L-14__openai | 2212 | 19.91 | 37.59 | ❌ |
| RN50x16__openai | 2221 | 15.87 | 34.75 | ❌ |
| ViT-B-16__openai | 985 | 5.03 | 32.1 | ❌ |
| RN50x4__openai | 1416 | 5.85 | 32.08 | ❌ |
| RN101__openai | 1111 | 3.21 | 30.77 | ❌ |
| RN50__openai | 913 | 2.39 | 30.2 | ✅ |
| ViT-B-32__openai | 1004 | 2.26 | 29.84 | ✅ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 85.47 | ✅ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 85.44 | ✅ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 85.32 | ✅ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 85.22 | ❌ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 85.15 | ❌ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 84.81 | ✅ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 84.68 | ❌ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 84.6 | ✅ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 84.55 | ✅ |
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 84.27 | ❌ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 84.15 | ✅ |
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 83.87 | ❌ |
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 83.74 | ❌ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 83.61 | ✅ |
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 83.15 | ❌ |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 81.7 | ❌ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 80.91 | ❌ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 80.73 | ✅ |
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 80.69 | ❌ |
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 80.3 | ❌ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 79.8 | ❌ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 79.71 | ✅ |
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 79.64 | ✅ |
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 78.0 | ✅ |
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 77.83 | ❌ |
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 76.87 | ✅ |
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 76.66 | ❌ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 75.99 | ✅ |
| ViT-SO400M-14-SigLIP-384__webli | 4417 | 72.19 | 71.96 | ❌ |
| ViT-H-14__laion2b-s32b-b79k | 4676 | 39.06 | 62.06 | ❌ |
| ViT-L-14__laion2b-s32b-b82k | 2233 | 20.56 | 53.78 | ❌ |
| ViT-L-14__laion400m_e32 | 2218 | 19.73 | 50.13 | ❌ |
| ViT-L-14__laion400m_e31 | 2183 | 19.87 | 50.0 | ❌ |
| ViT-B-16-plus-240__laion400m_e32 | 1246 | 6.95 | 47.39 | ❌ |
| ViT-B-16-plus-240__laion400m_e31 | 1263 | 6.94 | 47.39 | ❌ |
| ViT-B-32__laion2b_e16 | 1004 | 2.38 | 46.47 | ✅ |
| ViT-B-32__laion2b-s34b-b79k | 1001 | 2.29 | 45.68 | ✅ |
| ViT-B-16__laion400m_e31 | 991 | 5.04 | 44.0 | ✅ |
| ViT-B-16__laion400m_e32 | 975 | 4.98 | 43.98 | ✅ |
| ViT-B-32__laion400m_e32 | 1003 | 2.35 | 43.8 | ❌ |
| ViT-B-32__laion400m_e31 | 999 | 2.28 | 43.73 | ✅ |
| RN50x64__openai | 5079 | 48.79 | 43.01 | ❌ |
| ViT-L-14__openai | 2212 | 19.91 | 42.96 | ❌ |
| ViT-L-14-336__openai | 2616 | 43.45 | 41.67 | ❌ |
| RN50x16__openai | 2221 | 15.87 | 40.21 | ❌ |
| RN50x4__openai | 1416 | 5.85 | 36.06 | ❌ |
| ViT-B-16__openai | 985 | 5.03 | 35.67 | ❌ |
| RN101__openai | 1111 | 3.21 | 34.62 | ❌ |
| ViT-B-32__openai | 1004 | 2.26 | 32.6 | ✅ |
| RN50__openai | 913 | 2.39 | 31.79 | ✅ |
</details>
<details>
<summary>Swahili</summary>
@ -1057,8 +1066,8 @@ Memory and execution time estimates were obtained without acceleration on a 7800
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 72.1 | ✅ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 72.06 | ✅ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 71.84 | ✅ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 71.7 | ✅ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 71.7 | ✅ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 71.7 | ✅ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 71.61 | ❌ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 71.51 | ✅ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 71.45 | ✅ |
@ -1115,31 +1124,34 @@ Memory and execution time estimates were obtained without acceleration on a 7800
<summary>Turkish</summary>
| Model | Memory (MiB) | Execution Time (ms) | Recall (%) | Pareto Optimal |
|------------------------------------------------------|--------------|---------------------|------------|----------------|
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 81.15 | ✅ |
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 80.89 | ✅ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 78.11 | ✅ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 77.51 | ✅ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 77.36 | ✅ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 77.28 | ❌ |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 77.24 | ✅ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 77.01 | ✅ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 76.37 | ❌ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 75.92 | ✅ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 75.69 | ✅ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 75.68 | ❌ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 75.54 | ✅ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 75.16 | ✅ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 73.83 | ✅ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 70.15 | ✅ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 69.19 | ✅ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 66.72 | ❌ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 64.76 | ❌ |
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 38.8 | ❌ |
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 38.48 | ❌ |
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 30.83 | ❌ |
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 30.28 | ❌ |
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 21.31 | ✅ |
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 20.08 | ✅ |
| nllb-clip-large-siglip__mrl | 4248 | 75.44 | 83.91 | ✅ |
| nllb-clip-large-siglip__v1 | 4226 | 75.05 | 83.74 | ✅ |
| nllb-clip-base-siglip__mrl | 4696 | 16.95 | 81.26 | ✅ |
| nllb-clip-base-siglip__v1 | 4675 | 15.17 | 80.21 | ✅ |
| ViT-SO400M-16-SigLIP2-512__webli | 4050 | 107.67 | 79.34 | ✅ |
| ViT-SO400M-14-SigLIP2-378__webli | 3940 | 72.25 | 79.22 | ✅ |
| XLM-Roberta-Large-ViT-H-14__frozen_laion5b_s13b_b90k | 4014 | 39.14 | 78.9 | ✅ |
| ViT-SO400M-16-SigLIP2-384__webli | 3854 | 56.57 | 78.85 | ✅ |
| ViT-SO400M-16-SigLIP2-256__webli | 3611 | 27.84 | 78.29 | ✅ |
| ViT-gopt-16-SigLIP2-384__webli | 6585 | 146.84 | 78.27 | ❌ |
| ViT-gopt-16-SigLIP2-256__webli | 6475 | 64.51 | 78.0 | ❌ |
| ViT-SO400M-14-SigLIP2__webli | 3622 | 27.63 | 77.81 | ✅ |
| ViT-L-16-SigLIP2-512__webli | 3358 | 92.59 | 77.67 | ✅ |
| ViT-L-16-SigLIP2-384__webli | 3057 | 51.7 | 77.33 | ✅ |
| ViT-L-16-SigLIP2-256__webli | 2830 | 23.77 | 76.42 | ✅ |
| ViT-B-16-SigLIP-i18n-256__webli | 3029 | 6.87 | 72.44 | ✅ |
| XLM-Roberta-Base-ViT-B-32__laion5b_s13b_b90k | 3030 | 3.2 | 69.84 | ✅ |
| ViT-B-16-SigLIP2__webli | 3038 | 5.81 | 69.83 | ❌ |
| ViT-B-32-SigLIP2-256__webli | 3061 | 3.31 | 67.13 | ❌ |
| ViT-H-14-378-quickgelu__dfn5b | 5049 | 108.4 | 44.43 | ❌ |
| ViT-H-14-quickgelu__dfn5b | 4701 | 38.74 | 43.87 | ❌ |
| ViT-L-16-SigLIP-384__webli | 3396 | 47.6 | 35.1 | ❌ |
| ViT-L-16-SigLIP-256__webli | 3160 | 23.84 | 34.92 | ❌ |
| ViT-L-14-quickgelu__dfn2b | 2212 | 20.49 | 25.2 | ✅ |
| ViT-B-16-SigLIP-512__webli | 1828 | 26.17 | 24.55 | ✅ |
| ViT-B-16-SigLIP__webli | 1081 | 5.77 | 24.13 | ✅ |
| ViT-B-16-SigLIP-384__webli | 1128 | 13.53 | 24.08 | ❌ |
| ViT-B-16-SigLIP-256__webli | 1102 | 7.11 | 23.95 | ❌ |
</details>
<details>
<summary>Ukrainian</summary>

View File

@ -23,12 +23,12 @@ name: immich_remote_ml
services:
immich-machine-learning:
container_name: immich_machine_learning
# For hardware acceleration, add one of -[armnn, cuda, rocm, openvino] to the image tag.
# For hardware acceleration, add one of -[armnn, cuda, rocm, openvino, rknn] to the image tag.
# Example tag: ${IMMICH_VERSION:-release}-cuda
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
# extends:
# file: hwaccel.ml.yml
# service: # set to one of [armnn, cuda, rocm, openvino, openvino-wsl] for accelerated inference - use the `-wsl` version for WSL2 where applicable
# service: # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference - use the `-wsl` version for WSL2 where applicable
volumes:
- model-cache:/cache
restart: always

View File

@ -1,4 +1,8 @@
[
{
"label": "v1.130.3",
"url": "https://v1.130.3.archive.immich.app"
},
{
"label": "v1.130.2",
"url": "https://v1.130.2.archive.immich.app"

438
e2e/package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "immich-e2e",
"version": "1.130.2",
"version": "1.130.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "immich-e2e",
"version": "1.130.2",
"version": "1.130.3",
"license": "GNU Affero General Public License version 3",
"devDependencies": {
"@eslint/eslintrc": "^3.1.0",
@ -45,7 +45,7 @@
},
"../cli": {
"name": "@immich/cli",
"version": "2.2.56",
"version": "2.2.57",
"dev": true,
"license": "GNU Affero General Public License version 3",
"dependencies": {
@ -95,7 +95,7 @@
},
"../open-api/typescript-sdk": {
"name": "@immich/sdk",
"version": "1.130.2",
"version": "1.130.3",
"dev": true,
"license": "GNU Affero General Public License version 3",
"dependencies": {
@ -829,9 +829,9 @@
}
},
"node_modules/@eslint/config-helpers": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.1.0.tgz",
"integrity": "sha512-kLrdPDJE1ckPo94kmPPf9Hfd0DU0Jw6oKYrhe+pwSC0iTUInmTa+w6fw8sGgcfkFJGNdWOUeOaDM4quW4a7OkA==",
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.0.tgz",
"integrity": "sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@ -852,9 +852,9 @@
}
},
"node_modules/@eslint/eslintrc": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz",
"integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==",
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
"integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -889,9 +889,9 @@
}
},
"node_modules/@eslint/js": {
"version": "9.22.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.22.0.tgz",
"integrity": "sha512-vLFajx9o8d1/oL2ZkpMYbkLv8nDB6yaIwFNt7nI4+I80U/z03SxmfOMsLbvWr3p7C+Wnoh//aOu2pQW8cS0HCQ==",
"version": "9.23.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz",
"integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==",
"dev": true,
"license": "MIT",
"engines": {
@ -1258,13 +1258,13 @@
}
},
"node_modules/@playwright/test": {
"version": "1.51.0",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.51.0.tgz",
"integrity": "sha512-dJ0dMbZeHhI+wb77+ljx/FeC8VBP6j/rj9OAojO08JI80wTZy6vRk9KvHKiDCUh4iMpEiseMgqRBIeW+eKX6RA==",
"version": "1.51.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.51.1.tgz",
"integrity": "sha512-nM+kEaTSAoVlXmMPH10017vn3FSiFqr/bh4fKg9vmAdMfd9SDqRZNvPSiAHADc/itWak+qPvMPZQOPwCBW7k7Q==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.51.0"
"playwright": "1.51.1"
},
"bin": {
"playwright": "cli.js"
@ -1274,9 +1274,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.35.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.35.0.tgz",
"integrity": "sha512-uYQ2WfPaqz5QtVgMxfN6NpLD+no0MYHDBywl7itPYd3K5TjjSghNKmX8ic9S8NU8w81NVhJv/XojcHptRly7qQ==",
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.37.0.tgz",
"integrity": "sha512-l7StVw6WAa8l3vA1ov80jyetOAEo1FtHvZDbzXDO/02Sq/QVvqlHkYoFwDJPIMj0GKiistsBudfx5tGFnwYWDQ==",
"cpu": [
"arm"
],
@ -1288,9 +1288,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.35.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.35.0.tgz",
"integrity": "sha512-FtKddj9XZudurLhdJnBl9fl6BwCJ3ky8riCXjEw3/UIbjmIY58ppWwPEvU3fNu+W7FUsAsB1CdH+7EQE6CXAPA==",
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.37.0.tgz",
"integrity": "sha512-6U3SlVyMxezt8Y+/iEBcbp945uZjJwjZimu76xoG7tO1av9VO691z8PkhzQ85ith2I8R2RddEPeSfcbyPfD4hA==",
"cpu": [
"arm64"
],
@ -1302,9 +1302,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.35.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.35.0.tgz",
"integrity": "sha512-Uk+GjOJR6CY844/q6r5DR/6lkPFOw0hjfOIzVx22THJXMxktXG6CbejseJFznU8vHcEBLpiXKY3/6xc+cBm65Q==",
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.37.0.tgz",
"integrity": "sha512-+iTQ5YHuGmPt10NTzEyMPbayiNTcOZDWsbxZYR1ZnmLnZxG17ivrPSWFO9j6GalY0+gV3Jtwrrs12DBscxnlYA==",
"cpu": [
"arm64"
],
@ -1316,9 +1316,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.35.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.35.0.tgz",
"integrity": "sha512-3IrHjfAS6Vkp+5bISNQnPogRAW5GAV1n+bNCrDwXmfMHbPl5EhTmWtfmwlJxFRUCBZ+tZ/OxDyU08aF6NI/N5Q==",
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.37.0.tgz",
"integrity": "sha512-m8W2UbxLDcmRKVjgl5J/k4B8d7qX2EcJve3Sut7YGrQoPtCIQGPH5AMzuFvYRWZi0FVS0zEY4c8uttPfX6bwYQ==",
"cpu": [
"x64"
],
@ -1330,9 +1330,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.35.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.35.0.tgz",
"integrity": "sha512-sxjoD/6F9cDLSELuLNnY0fOrM9WA0KrM0vWm57XhrIMf5FGiN8D0l7fn+bpUeBSU7dCgPV2oX4zHAsAXyHFGcQ==",
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.37.0.tgz",
"integrity": "sha512-FOMXGmH15OmtQWEt174v9P1JqqhlgYge/bUjIbiVD1nI1NeJ30HYT9SJlZMqdo1uQFyt9cz748F1BHghWaDnVA==",
"cpu": [
"arm64"
],
@ -1344,9 +1344,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.35.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.35.0.tgz",
"integrity": "sha512-2mpHCeRuD1u/2kruUiHSsnjWtHjqVbzhBkNVQ1aVD63CcexKVcQGwJ2g5VphOd84GvxfSvnnlEyBtQCE5hxVVw==",
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.37.0.tgz",
"integrity": "sha512-SZMxNttjPKvV14Hjck5t70xS3l63sbVwl98g3FlVVx2YIDmfUIy29jQrsw06ewEYQ8lQSuY9mpAPlmgRD2iSsA==",
"cpu": [
"x64"
],
@ -1358,9 +1358,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.35.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.35.0.tgz",
"integrity": "sha512-mrA0v3QMy6ZSvEuLs0dMxcO2LnaCONs1Z73GUDBHWbY8tFFocM6yl7YyMu7rz4zS81NDSqhrUuolyZXGi8TEqg==",
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.37.0.tgz",
"integrity": "sha512-hhAALKJPidCwZcj+g+iN+38SIOkhK2a9bqtJR+EtyxrKKSt1ynCBeqrQy31z0oWU6thRZzdx53hVgEbRkuI19w==",
"cpu": [
"arm"
],
@ -1372,9 +1372,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.35.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.35.0.tgz",
"integrity": "sha512-DnYhhzcvTAKNexIql8pFajr0PiDGrIsBYPRvCKlA5ixSS3uwo/CWNZxB09jhIapEIg945KOzcYEAGGSmTSpk7A==",
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.37.0.tgz",
"integrity": "sha512-jUb/kmn/Gd8epbHKEqkRAxq5c2EwRt0DqhSGWjPFxLeFvldFdHQs/n8lQ9x85oAeVb6bHcS8irhTJX2FCOd8Ag==",
"cpu": [
"arm"
],
@ -1386,9 +1386,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.35.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.35.0.tgz",
"integrity": "sha512-uagpnH2M2g2b5iLsCTZ35CL1FgyuzzJQ8L9VtlJ+FckBXroTwNOaD0z0/UF+k5K3aNQjbm8LIVpxykUOQt1m/A==",
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.37.0.tgz",
"integrity": "sha512-oNrJxcQT9IcbcmKlkF+Yz2tmOxZgG9D9GRq+1OE6XCQwCVwxixYAa38Z8qqPzQvzt1FCfmrHX03E0pWoXm1DqA==",
"cpu": [
"arm64"
],
@ -1400,9 +1400,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.35.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.35.0.tgz",
"integrity": "sha512-XQxVOCd6VJeHQA/7YcqyV0/88N6ysSVzRjJ9I9UA/xXpEsjvAgDTgH3wQYz5bmr7SPtVK2TsP2fQ2N9L4ukoUg==",
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.37.0.tgz",
"integrity": "sha512-pfxLBMls+28Ey2enpX3JvjEjaJMBX5XlPCZNGxj4kdJyHduPBXtxYeb8alo0a7bqOoWZW2uKynhHxF/MWoHaGQ==",
"cpu": [
"arm64"
],
@ -1414,9 +1414,9 @@
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.35.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.35.0.tgz",
"integrity": "sha512-5pMT5PzfgwcXEwOaSrqVsz/LvjDZt+vQ8RT/70yhPU06PTuq8WaHhfT1LW+cdD7mW6i/J5/XIkX/1tCAkh1W6g==",
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.37.0.tgz",
"integrity": "sha512-yCE0NnutTC/7IGUq/PUHmoeZbIwq3KRh02e9SfFh7Vmc1Z7atuJRYWhRME5fKgT8aS20mwi1RyChA23qSyRGpA==",
"cpu": [
"loong64"
],
@ -1428,9 +1428,9 @@
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.35.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.35.0.tgz",
"integrity": "sha512-c+zkcvbhbXF98f4CtEIP1EBA/lCic5xB0lToneZYvMeKu5Kamq3O8gqrxiYYLzlZH6E3Aq+TSW86E4ay8iD8EA==",
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.37.0.tgz",
"integrity": "sha512-NxcICptHk06E2Lh3a4Pu+2PEdZ6ahNHuK7o6Np9zcWkrBMuv21j10SQDJW3C9Yf/A/P7cutWoC/DptNLVsZ0VQ==",
"cpu": [
"ppc64"
],
@ -1442,9 +1442,23 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.35.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.35.0.tgz",
"integrity": "sha512-s91fuAHdOwH/Tad2tzTtPX7UZyytHIRR6V4+2IGlV0Cej5rkG0R61SX4l4y9sh0JBibMiploZx3oHKPnQBKe4g==",
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.37.0.tgz",
"integrity": "sha512-PpWwHMPCVpFZLTfLq7EWJWvrmEuLdGn1GMYcm5MV7PaRgwCEYJAwiN94uBuZev0/J/hFIIJCsYw4nLmXA9J7Pw==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.37.0.tgz",
"integrity": "sha512-DTNwl6a3CfhGTAOYZ4KtYbdS8b+275LSLqJVJIrPa5/JuIufWWZ/QFvkxp52gpmguN95eujrM68ZG+zVxa8zHA==",
"cpu": [
"riscv64"
],
@ -1456,9 +1470,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.35.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.35.0.tgz",
"integrity": "sha512-hQRkPQPLYJZYGP+Hj4fR9dDBMIM7zrzJDWFEMPdTnTy95Ljnv0/4w/ixFw3pTBMEuuEuoqtBINYND4M7ujcuQw==",
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.37.0.tgz",
"integrity": "sha512-hZDDU5fgWvDdHFuExN1gBOhCuzo/8TMpidfOR+1cPZJflcEzXdCy1LjnklQdW8/Et9sryOPJAKAQRw8Jq7Tg+A==",
"cpu": [
"s390x"
],
@ -1470,9 +1484,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.35.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.35.0.tgz",
"integrity": "sha512-Pim1T8rXOri+0HmV4CdKSGrqcBWX0d1HoPnQ0uw0bdp1aP5SdQVNBy8LjYncvnLgu3fnnCt17xjWGd4cqh8/hA==",
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.37.0.tgz",
"integrity": "sha512-pKivGpgJM5g8dwj0ywBwe/HeVAUSuVVJhUTa/URXjxvoyTT/AxsLTAbkHkDHG7qQxLoW2s3apEIl26uUe08LVQ==",
"cpu": [
"x64"
],
@ -1484,9 +1498,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.35.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.35.0.tgz",
"integrity": "sha512-QysqXzYiDvQWfUiTm8XmJNO2zm9yC9P/2Gkrwg2dH9cxotQzunBHYr6jk4SujCTqnfGxduOmQcI7c2ryuW8XVg==",
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.37.0.tgz",
"integrity": "sha512-E2lPrLKE8sQbY/2bEkVTGDEk4/49UYRVWgj90MY8yPjpnGBQ+Xi1Qnr7b7UIWw1NOggdFQFOLZ8+5CzCiz143w==",
"cpu": [
"x64"
],
@ -1498,9 +1512,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.35.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.35.0.tgz",
"integrity": "sha512-OUOlGqPkVJCdJETKOCEf1mw848ZyJ5w50/rZ/3IBQVdLfR5jk/6Sr5m3iO2tdPgwo0x7VcncYuOvMhBWZq8ayg==",
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.37.0.tgz",
"integrity": "sha512-Jm7biMazjNzTU4PrQtr7VS8ibeys9Pn29/1bm4ph7CP2kf21950LgN+BaE2mJ1QujnvOc6p54eWWiVvn05SOBg==",
"cpu": [
"arm64"
],
@ -1512,9 +1526,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.35.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.35.0.tgz",
"integrity": "sha512-2/lsgejMrtwQe44glq7AFFHLfJBPafpsTa6JvP2NGef/ifOa4KBoglVf7AKN7EV9o32evBPRqfg96fEHzWo5kw==",
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.37.0.tgz",
"integrity": "sha512-e3/1SFm1OjefWICB2Ucstg2dxYDkDTZGDYgwufcbsxTHyqQps1UQf33dFEChBNmeSsTOyrjw2JJq0zbG5GF6RA==",
"cpu": [
"ia32"
],
@ -1526,9 +1540,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.35.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.35.0.tgz",
"integrity": "sha512-PIQeY5XDkrOysbQblSW7v3l1MDZzkTEzAfTPkj5VAu3FW8fS4ynyLg2sINp0fp3SjZ8xkRYpLqoKcYqAkhU1dw==",
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.37.0.tgz",
"integrity": "sha512-LWbXUBwn/bcLx2sSsqy7pK5o+Nr+VCoRoAohfJ5C/aBio9nfJmGQqHAhU6pwxV/RmyTk5AqdySma7uwWGlmeuA==",
"cpu": [
"x64"
],
@ -1892,17 +1906,17 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.26.1.tgz",
"integrity": "sha512-2X3mwqsj9Bd3Ciz508ZUtoQQYpOhU/kWoUqIf49H8Z0+Vbh6UF/y0OEYp0Q0axOGzaBGs7QxRwq0knSQ8khQNA==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.27.0.tgz",
"integrity": "sha512-4henw4zkePi5p252c8ncBLzLce52SEUz2Ebj8faDnuUXz2UuHEONYcJ+G0oaCF+bYCWVZtrGzq3FD7YXetmnSA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.26.1",
"@typescript-eslint/type-utils": "8.26.1",
"@typescript-eslint/utils": "8.26.1",
"@typescript-eslint/visitor-keys": "8.26.1",
"@typescript-eslint/scope-manager": "8.27.0",
"@typescript-eslint/type-utils": "8.27.0",
"@typescript-eslint/utils": "8.27.0",
"@typescript-eslint/visitor-keys": "8.27.0",
"graphemer": "^1.4.0",
"ignore": "^5.3.1",
"natural-compare": "^1.4.0",
@ -1922,16 +1936,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.26.1.tgz",
"integrity": "sha512-w6HZUV4NWxqd8BdeFf81t07d7/YV9s7TCWrQQbG5uhuvGUAW+fq1usZ1Hmz9UPNLniFnD8GLSsDpjP0hm1S4lQ==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.27.0.tgz",
"integrity": "sha512-XGwIabPallYipmcOk45DpsBSgLC64A0yvdAkrwEzwZ2viqGqRUJ8eEYoPz0CWnutgAFbNMPdsGGvzjSmcWVlEA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.26.1",
"@typescript-eslint/types": "8.26.1",
"@typescript-eslint/typescript-estree": "8.26.1",
"@typescript-eslint/visitor-keys": "8.26.1",
"@typescript-eslint/scope-manager": "8.27.0",
"@typescript-eslint/types": "8.27.0",
"@typescript-eslint/typescript-estree": "8.27.0",
"@typescript-eslint/visitor-keys": "8.27.0",
"debug": "^4.3.4"
},
"engines": {
@ -1947,14 +1961,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.26.1.tgz",
"integrity": "sha512-6EIvbE5cNER8sqBu6V7+KeMZIC1664d2Yjt+B9EWUXrsyWpxx4lEZrmvxgSKRC6gX+efDL/UY9OpPZ267io3mg==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.27.0.tgz",
"integrity": "sha512-8oI9GwPMQmBryaaxG1tOZdxXVeMDte6NyJA4i7/TWa4fBwgnAXYlIQP+uYOeqAaLJ2JRxlG9CAyL+C+YE9Xknw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.26.1",
"@typescript-eslint/visitor-keys": "8.26.1"
"@typescript-eslint/types": "8.27.0",
"@typescript-eslint/visitor-keys": "8.27.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -1965,14 +1979,14 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.26.1.tgz",
"integrity": "sha512-Kcj/TagJLwoY/5w9JGEFV0dclQdyqw9+VMndxOJKtoFSjfZhLXhYjzsQEeyza03rwHx2vFEGvrJWJBXKleRvZg==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.27.0.tgz",
"integrity": "sha512-wVArTVcz1oJOIEJxui/nRhV0TXzD/zMSOYi/ggCfNq78EIszddXcJb7r4RCp/oBrjt8n9A0BSxRMKxHftpDxDA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/typescript-estree": "8.26.1",
"@typescript-eslint/utils": "8.26.1",
"@typescript-eslint/typescript-estree": "8.27.0",
"@typescript-eslint/utils": "8.27.0",
"debug": "^4.3.4",
"ts-api-utils": "^2.0.1"
},
@ -1989,9 +2003,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.26.1.tgz",
"integrity": "sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.27.0.tgz",
"integrity": "sha512-/6cp9yL72yUHAYq9g6DsAU+vVfvQmd1a8KyA81uvfDE21O2DwQ/qxlM4AR8TSdAu+kJLBDrEHKC5/W2/nxsY0A==",
"dev": true,
"license": "MIT",
"engines": {
@ -2003,14 +2017,14 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.26.1.tgz",
"integrity": "sha512-yUwPpUHDgdrv1QJ7YQal3cMVBGWfnuCdKbXw1yyjArax3353rEJP1ZA+4F8nOlQ3RfS2hUN/wze3nlY+ZOhvoA==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.27.0.tgz",
"integrity": "sha512-BnKq8cqPVoMw71O38a1tEb6iebEgGA80icSxW7g+kndx0o6ot6696HjG7NdgfuAVmVEtwXUr3L8R9ZuVjoQL6A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.26.1",
"@typescript-eslint/visitor-keys": "8.26.1",
"@typescript-eslint/types": "8.27.0",
"@typescript-eslint/visitor-keys": "8.27.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
@ -2056,16 +2070,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.1.tgz",
"integrity": "sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.27.0.tgz",
"integrity": "sha512-njkodcwH1yvmo31YWgRHNb/x1Xhhq4/m81PhtvmRngD8iHPehxffz1SNCO+kwaePhATC+kOa/ggmvPoPza5i0Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@typescript-eslint/scope-manager": "8.26.1",
"@typescript-eslint/types": "8.26.1",
"@typescript-eslint/typescript-estree": "8.26.1"
"@typescript-eslint/scope-manager": "8.27.0",
"@typescript-eslint/types": "8.27.0",
"@typescript-eslint/typescript-estree": "8.27.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -2080,13 +2094,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.1.tgz",
"integrity": "sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.27.0.tgz",
"integrity": "sha512-WsXQwMkILJvffP6z4U3FYJPlbf/j07HIxmDjZpbNvBJkMfvwXj5ACRkkHwBDvLBbDbtX5TdU64/rcvKJ/vuInQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.26.1",
"@typescript-eslint/types": "8.27.0",
"eslint-visitor-keys": "^4.2.0"
},
"engines": {
@ -2111,9 +2125,9 @@
}
},
"node_modules/@vitest/coverage-v8": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.8.tgz",
"integrity": "sha512-y7SAKsQirsEJ2F8bulBck4DoluhI2EEgTimHd6EEUgJBGKy9tC25cpywh1MH4FvDGoG2Unt7+asVd1kj4qOSAw==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.9.tgz",
"integrity": "sha512-15OACZcBtQ34keIEn19JYTVuMFTlFrClclwWjHo/IRPg/8ELpkgNTl0o7WLP9WO9XGH6+tip9CPYtEOrIDJvBA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -2134,8 +2148,8 @@
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"@vitest/browser": "3.0.8",
"vitest": "3.0.8"
"@vitest/browser": "3.0.9",
"vitest": "3.0.9"
},
"peerDependenciesMeta": {
"@vitest/browser": {
@ -2162,14 +2176,14 @@
}
},
"node_modules/@vitest/expect": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.8.tgz",
"integrity": "sha512-Xu6TTIavTvSSS6LZaA3EebWFr6tsoXPetOWNMOlc7LO88QVVBwq2oQWBoDiLCN6YTvNYsGSjqOO8CAdjom5DCQ==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.9.tgz",
"integrity": "sha512-5eCqRItYgIML7NNVgJj6TVCmdzE7ZVgJhruW0ziSQV4V7PvLkDL1bBkBdcTs/VuIz0IxPb5da1IDSqc1TR9eig==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/spy": "3.0.8",
"@vitest/utils": "3.0.8",
"@vitest/spy": "3.0.9",
"@vitest/utils": "3.0.9",
"chai": "^5.2.0",
"tinyrainbow": "^2.0.0"
},
@ -2178,13 +2192,13 @@
}
},
"node_modules/@vitest/mocker": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.8.tgz",
"integrity": "sha512-n3LjS7fcW1BCoF+zWZxG7/5XvuYH+lsFg+BDwwAz0arIwHQJFUEsKBQ0BLU49fCxuM/2HSeBPHQD8WjgrxMfow==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.9.tgz",
"integrity": "sha512-ryERPIBOnvevAkTq+L1lD+DTFBRcjueL9lOUfXsLfwP92h4e+Heb+PjiqS3/OURWPtywfafK0kj++yDFjWUmrA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/spy": "3.0.8",
"@vitest/spy": "3.0.9",
"estree-walker": "^3.0.3",
"magic-string": "^0.30.17"
},
@ -2205,9 +2219,9 @@
}
},
"node_modules/@vitest/pretty-format": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.8.tgz",
"integrity": "sha512-BNqwbEyitFhzYMYHUVbIvepOyeQOSFA/NeJMIP9enMntkkxLgOcgABH6fjyXG85ipTgvero6noreavGIqfJcIg==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.9.tgz",
"integrity": "sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -2218,13 +2232,13 @@
}
},
"node_modules/@vitest/runner": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.8.tgz",
"integrity": "sha512-c7UUw6gEcOzI8fih+uaAXS5DwjlBaCJUo7KJ4VvJcjL95+DSR1kova2hFuRt3w41KZEFcOEiq098KkyrjXeM5w==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.9.tgz",
"integrity": "sha512-NX9oUXgF9HPfJSwl8tUZCMP1oGx2+Sf+ru6d05QjzQz4OwWg0psEzwY6VexP2tTHWdOkhKHUIZH+fS6nA7jfOw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/utils": "3.0.8",
"@vitest/utils": "3.0.9",
"pathe": "^2.0.3"
},
"funding": {
@ -2232,13 +2246,13 @@
}
},
"node_modules/@vitest/snapshot": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.8.tgz",
"integrity": "sha512-x8IlMGSEMugakInj44nUrLSILh/zy1f2/BgH0UeHpNyOocG18M9CWVIFBaXPt8TrqVZWmcPjwfG/ht5tnpba8A==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.9.tgz",
"integrity": "sha512-AiLUiuZ0FuA+/8i19mTYd+re5jqjEc2jZbgJ2up0VY0Ddyyxg/uUtBDpIFAy4uzKaQxOW8gMgBdAJJ2ydhu39A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "3.0.8",
"@vitest/pretty-format": "3.0.9",
"magic-string": "^0.30.17",
"pathe": "^2.0.3"
},
@ -2247,9 +2261,9 @@
}
},
"node_modules/@vitest/spy": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.8.tgz",
"integrity": "sha512-MR+PzJa+22vFKYb934CejhR4BeRpMSoxkvNoDit68GQxRLSf11aT6CTj3XaqUU9rxgWJFnqicN/wxw6yBRkI1Q==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.9.tgz",
"integrity": "sha512-/CcK2UDl0aQ2wtkp3YVWldrpLRNCfVcIOFGlVGKO4R5eajsH393Z1yiXLVQ7vWsj26JOEjeZI0x5sm5P4OGUNQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -2260,13 +2274,13 @@
}
},
"node_modules/@vitest/utils": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.8.tgz",
"integrity": "sha512-nkBC3aEhfX2PdtQI/QwAWp8qZWwzASsU4Npbcd5RdMPBSSLCpkZp52P3xku3s3uA0HIEhGvEcF8rNkBsz9dQ4Q==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.9.tgz",
"integrity": "sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "3.0.8",
"@vitest/pretty-format": "3.0.9",
"loupe": "^3.1.3",
"tinyrainbow": "^2.0.0"
},
@ -3167,19 +3181,19 @@
}
},
"node_modules/eslint": {
"version": "9.22.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.22.0.tgz",
"integrity": "sha512-9V/QURhsRN40xuHXWjV64yvrzMjcz7ZyNoF2jJFmy9j/SLk0u1OLSZgXi28MrXjymnjEGSR80WCdab3RGMDveQ==",
"version": "9.23.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz",
"integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.19.2",
"@eslint/config-helpers": "^0.1.0",
"@eslint/config-helpers": "^0.2.0",
"@eslint/core": "^0.12.0",
"@eslint/eslintrc": "^3.3.0",
"@eslint/js": "9.22.0",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.23.0",
"@eslint/plugin-kit": "^0.2.7",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
@ -5222,9 +5236,9 @@
}
},
"node_modules/pg": {
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.14.0.tgz",
"integrity": "sha512-nXbVpyoaXVmdqlKEzToFf37qzyeeh7mbiXsnoWvstSqohj88yaa/I/Rq/HEVn2QPSZEuLIJa/jSpRDyzjEx4FQ==",
"version": "8.14.1",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.14.1.tgz",
"integrity": "sha512-0TdbqfjwIun9Fm/r89oB7RFQ0bLgduAhiIqIXOsyKoiC/L54DbuAAzIEN/9Op0f1Po9X7iCPXGoa/Ah+2aI8Xw==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -5344,13 +5358,13 @@
}
},
"node_modules/playwright": {
"version": "1.51.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.51.0.tgz",
"integrity": "sha512-442pTfGM0xxfCYxuBa/Pu6B2OqxqqaYq39JS8QDMGThUvIOCd6s0ANDog3uwA0cHavVlnTQzGCN7Id2YekDSXA==",
"version": "1.51.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.51.1.tgz",
"integrity": "sha512-kkx+MB2KQRkyxjYPc3a0wLZZoDczmppyGJIvQ43l+aZihkaVvmu/21kiyaHeHjiFxjxNNFnUncKmcGIyOojsaw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.51.0"
"playwright-core": "1.51.1"
},
"bin": {
"playwright": "cli.js"
@ -5363,9 +5377,9 @@
}
},
"node_modules/playwright-core": {
"version": "1.51.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.51.0.tgz",
"integrity": "sha512-x47yPE3Zwhlil7wlNU/iktF7t2r/URR3VLbH6EknJd/04Qc/PSJ0EY3CMXipmglLG+zyRxW6HNo2EGbKLHPWMg==",
"version": "1.51.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.51.1.tgz",
"integrity": "sha512-/crRMj8+j/Nq5s8QcvegseuyeZPxpQCZb6HNk3Sos3BlZyAknRjoyJPFWkpNn8v0+P3WiwqFF8P+zQo4eqiNuw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@ -5423,9 +5437,9 @@
}
},
"node_modules/postcss/node_modules/nanoid": {
"version": "3.3.9",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.9.tgz",
"integrity": "sha512-SppoicMGpZvbF1l3z4x7No3OlIjP7QJvC9XR7AhZr1kL133KHnKPztkKDc+Ir4aJ/1VhTySrtKhrsycmrMQfvg==",
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true,
"funding": [
{
@ -5833,9 +5847,9 @@
}
},
"node_modules/rollup": {
"version": "4.35.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.35.0.tgz",
"integrity": "sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg==",
"version": "4.37.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.37.0.tgz",
"integrity": "sha512-iAtQy/L4QFU+rTJ1YUjXqJOJzuwEghqWzCEYD2FEghT7Gsy1VdABntrO4CLopA5IkflTyqNiLNwPcOJ3S7UKLg==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -5849,25 +5863,26 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.35.0",
"@rollup/rollup-android-arm64": "4.35.0",
"@rollup/rollup-darwin-arm64": "4.35.0",
"@rollup/rollup-darwin-x64": "4.35.0",
"@rollup/rollup-freebsd-arm64": "4.35.0",
"@rollup/rollup-freebsd-x64": "4.35.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.35.0",
"@rollup/rollup-linux-arm-musleabihf": "4.35.0",
"@rollup/rollup-linux-arm64-gnu": "4.35.0",
"@rollup/rollup-linux-arm64-musl": "4.35.0",
"@rollup/rollup-linux-loongarch64-gnu": "4.35.0",
"@rollup/rollup-linux-powerpc64le-gnu": "4.35.0",
"@rollup/rollup-linux-riscv64-gnu": "4.35.0",
"@rollup/rollup-linux-s390x-gnu": "4.35.0",
"@rollup/rollup-linux-x64-gnu": "4.35.0",
"@rollup/rollup-linux-x64-musl": "4.35.0",
"@rollup/rollup-win32-arm64-msvc": "4.35.0",
"@rollup/rollup-win32-ia32-msvc": "4.35.0",
"@rollup/rollup-win32-x64-msvc": "4.35.0",
"@rollup/rollup-android-arm-eabi": "4.37.0",
"@rollup/rollup-android-arm64": "4.37.0",
"@rollup/rollup-darwin-arm64": "4.37.0",
"@rollup/rollup-darwin-x64": "4.37.0",
"@rollup/rollup-freebsd-arm64": "4.37.0",
"@rollup/rollup-freebsd-x64": "4.37.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.37.0",
"@rollup/rollup-linux-arm-musleabihf": "4.37.0",
"@rollup/rollup-linux-arm64-gnu": "4.37.0",
"@rollup/rollup-linux-arm64-musl": "4.37.0",
"@rollup/rollup-linux-loongarch64-gnu": "4.37.0",
"@rollup/rollup-linux-powerpc64le-gnu": "4.37.0",
"@rollup/rollup-linux-riscv64-gnu": "4.37.0",
"@rollup/rollup-linux-riscv64-musl": "4.37.0",
"@rollup/rollup-linux-s390x-gnu": "4.37.0",
"@rollup/rollup-linux-x64-gnu": "4.37.0",
"@rollup/rollup-linux-x64-musl": "4.37.0",
"@rollup/rollup-win32-arm64-msvc": "4.37.0",
"@rollup/rollup-win32-ia32-msvc": "4.37.0",
"@rollup/rollup-win32-x64-msvc": "4.37.0",
"fsevents": "~2.3.2"
}
},
@ -6232,10 +6247,11 @@
}
},
"node_modules/supertest": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/supertest/-/supertest-7.0.0.tgz",
"integrity": "sha512-qlsr7fIC0lSddmA3tzojvzubYxvlGtzumcdHgPwbFWMISQwL22MhM2Y3LNt+6w9Yyx7559VW5ab70dgphm8qQA==",
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.0.tgz",
"integrity": "sha512-5QeSO8hSrKghtcWEoPiO036fxH0Ii2wVQfFZSP0oqQhmjk8bOLhDFXr4JrvaFmPuEWUoq4znY3uSi8UzLKxGqw==",
"dev": true,
"license": "MIT",
"dependencies": {
"methods": "^1.1.2",
"superagent": "^9.0.1"
@ -6604,9 +6620,9 @@
}
},
"node_modules/vite": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.1.tgz",
"integrity": "sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q==",
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.3.tgz",
"integrity": "sha512-IzwM54g4y9JA/xAeBPNaDXiBF8Jsgl3VBQ2YQ/wOY6fyW3xMdSoltIV3Bo59DErdqdE6RxUfv8W69DvUorE4Eg==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -6676,9 +6692,9 @@
}
},
"node_modules/vite-node": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.8.tgz",
"integrity": "sha512-6PhR4H9VGlcwXZ+KWCdMqbtG649xCPZqfI9j2PsK1FcXgEzro5bGHcVKFCTqPLaNKZES8Evqv4LwvZARsq5qlg==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.9.tgz",
"integrity": "sha512-w3Gdx7jDcuT9cNn9jExXgOyKmf5UOTb6WMHz8LGAm54eS1Elf5OuBhCxl6zJxGhEeIkgsE1WbHuoL0mj/UXqXg==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -6732,19 +6748,19 @@
}
},
"node_modules/vitest": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.8.tgz",
"integrity": "sha512-dfqAsNqRGUc8hB9OVR2P0w8PZPEckti2+5rdZip0WIz9WW0MnImJ8XiR61QhqLa92EQzKP2uPkzenKOAHyEIbA==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.9.tgz",
"integrity": "sha512-BbcFDqNyBlfSpATmTtXOAOj71RNKDDvjBM/uPfnxxVGrG+FSH2RQIwgeEngTaTkuU/h0ScFvf+tRcKfYXzBybQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/expect": "3.0.8",
"@vitest/mocker": "3.0.8",
"@vitest/pretty-format": "^3.0.8",
"@vitest/runner": "3.0.8",
"@vitest/snapshot": "3.0.8",
"@vitest/spy": "3.0.8",
"@vitest/utils": "3.0.8",
"@vitest/expect": "3.0.9",
"@vitest/mocker": "3.0.9",
"@vitest/pretty-format": "^3.0.9",
"@vitest/runner": "3.0.9",
"@vitest/snapshot": "3.0.9",
"@vitest/spy": "3.0.9",
"@vitest/utils": "3.0.9",
"chai": "^5.2.0",
"debug": "^4.4.0",
"expect-type": "^1.1.0",
@ -6756,7 +6772,7 @@
"tinypool": "^1.0.2",
"tinyrainbow": "^2.0.0",
"vite": "^5.0.0 || ^6.0.0",
"vite-node": "3.0.8",
"vite-node": "3.0.9",
"why-is-node-running": "^2.3.0"
},
"bin": {
@ -6772,8 +6788,8 @@
"@edge-runtime/vm": "*",
"@types/debug": "^4.1.12",
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
"@vitest/browser": "3.0.8",
"@vitest/ui": "3.0.8",
"@vitest/browser": "3.0.9",
"@vitest/ui": "3.0.9",
"happy-dom": "*",
"jsdom": "*"
},

View File

@ -1,6 +1,6 @@
{
"name": "immich-e2e",
"version": "1.130.2",
"version": "1.130.3",
"description": "",
"main": "index.js",
"type": "module",

View File

@ -51,7 +51,6 @@ ARG DEVICE
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
VIRTUAL_ENV=/opt/venv
WORKDIR /usr/src/app
RUN apt-get update && apt-get install -y --no-install-recommends g++
@ -66,6 +65,8 @@ RUN if [ "$DEVICE" = "rocm" ]; then \
FROM python:3.11-slim-bookworm@sha256:7029b00486ac40bed03e36775b864d3f3d39dcbdf19cd45e6a52d541e6c178f0 AS prod-cpu
ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2
FROM prod-cpu AS prod-openvino
RUN apt-get update && \
@ -94,7 +95,8 @@ FROM rocm/dev-ubuntu-22.04:6.3.4-complete@sha256:1f7e92ca7e3a3785680473329ed1091
FROM prod-cpu AS prod-armnn
ENV LD_LIBRARY_PATH=/opt/armnn
ENV LD_LIBRARY_PATH=/opt/armnn \
LD_PRELOAD=/usr/lib/libmimalloc.so.2
RUN apt-get update && apt-get install -y --no-install-recommends ocl-icd-libopencl1 mesa-opencl-icd libgomp1 && \
rm -rf /var/lib/apt/lists/* && \
@ -114,6 +116,8 @@ COPY --from=builder-armnn \
FROM prod-cpu AS prod-rknn
ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2
ADD --checksum=sha256:73993ed4b440460825f21611731564503cc1d5a0c123746477da6cd574f34885 https://github.com/airockchip/rknn-toolkit2/raw/refs/tags/v2.3.0/rknpu2/runtime/Linux/librknn_api/aarch64/librknnrt.so /usr/lib/
FROM prod-${DEVICE} AS prod
@ -126,14 +130,18 @@ RUN apt-get update && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
WORKDIR /usr/src/app
RUN ln -s "/usr/lib/$(arch)-linux-gnu/libmimalloc.so.2" /usr/lib/libmimalloc.so.2
WORKDIR /usr/src
ENV TRANSFORMERS_CACHE=/cache \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PATH="/opt/venv/bin:$PATH" \
PYTHONPATH=/usr/src \
DEVICE=${DEVICE} \
VIRTUAL_ENV=/opt/venv
VIRTUAL_ENV=/opt/venv \
LD_BIND_NOW=1 \
MACHINE_LEARNING_CACHE_FOLDER=/cache
# prevent core dumps
RUN echo "hard core 0" >> /etc/security/limits.conf && \
@ -141,9 +149,7 @@ RUN echo "hard core 0" >> /etc/security/limits.conf && \
echo 'ulimit -S -c 0 > /dev/null 2>&1' >> /etc/profile
COPY --from=builder /opt/venv /opt/venv
COPY ann/ann.py /usr/src/ann/ann.py
COPY start.sh log_conf.json gunicorn_conf.py ./
COPY app .
COPY immich_ml immich_ml
ARG BUILD_ID
ARG BUILD_IMAGE
@ -161,6 +167,6 @@ ENV IMMICH_SOURCE_COMMIT=${BUILD_SOURCE_COMMIT}
ENV IMMICH_SOURCE_URL=https://github.com/immich-app/immich/commit/${BUILD_SOURCE_COMMIT}
ENTRYPOINT ["tini", "--"]
CMD ["./start.sh"]
CMD ["python", "-m", "immich_ml"]
HEALTHCHECK CMD python3 healthcheck.py
HEALTHCHECK CMD python3 healthcheck.py

View File

@ -8,9 +8,8 @@ from fastapi.testclient import TestClient
from numpy.typing import NDArray
from PIL import Image
from app.config import log
from .main import app
from immich_ml.config import log
from immich_ml.main import app
@pytest.fixture
@ -25,7 +24,7 @@ def cv_image(pil_image: Image.Image) -> NDArray[np.float32]:
@pytest.fixture
def mock_get_model() -> Iterator[mock.Mock]:
with mock.patch("app.models.cache.from_model_type", autospec=True) as mocked:
with mock.patch("immich_ml.models.cache.from_model_type", autospec=True) as mocked:
yield mocked
@ -104,14 +103,14 @@ def providers(request: pytest.FixtureRequest) -> Iterator[mock.Mock]:
raise ValueError("Missing marker 'providers'")
providers = marker.args[0]
with mock.patch("app.sessions.ort.ort.get_available_providers") as mocked:
with mock.patch("immich_ml.sessions.ort.ort.get_available_providers") as mocked:
mocked.return_value = providers
yield providers
@pytest.fixture(scope="function")
def ort_pybind() -> Iterator[mock.Mock]:
with mock.patch("app.sessions.ort.ort.capi._pybind_state") as mocked:
with mock.patch("immich_ml.sessions.ort.ort.capi._pybind_state") as mocked:
yield mocked
@ -126,25 +125,25 @@ def ov_device_ids(request: pytest.FixtureRequest, ort_pybind: mock.Mock) -> Iter
@pytest.fixture(scope="function")
def ort_session() -> Iterator[mock.Mock]:
with mock.patch("app.sessions.ort.ort.InferenceSession") as mocked:
with mock.patch("immich_ml.sessions.ort.ort.InferenceSession") as mocked:
yield mocked
@pytest.fixture(scope="function")
def ann_session() -> Iterator[mock.Mock]:
with mock.patch("app.sessions.ann.Ann") as mocked:
with mock.patch("immich_ml.sessions.ann.Ann") as mocked:
yield mocked
@pytest.fixture(scope="function")
def rknn_session() -> Iterator[mock.Mock]:
with mock.patch("app.sessions.rknn.RknnPoolExecutor") as mocked:
with mock.patch("immich_ml.sessions.rknn.RknnPoolExecutor") as mocked:
yield mocked
@pytest.fixture(scope="function")
def rmtree() -> Iterator[mock.Mock]:
with mock.patch("app.models.base.rmtree", autospec=True) as mocked:
with mock.patch("immich_ml.models.base.rmtree", autospec=True) as mocked:
mocked.avoids_symlink_attacks = True
yield mocked
@ -158,7 +157,7 @@ def path() -> Iterator[mock.Mock]:
path.with_suffix.return_value = path
path.return_value = path
with mock.patch("app.models.base.Path", return_value=path) as mocked:
with mock.patch("immich_ml.models.base.Path", return_value=path) as mocked:
yield mocked
@ -182,5 +181,5 @@ def exception() -> Iterator[mock.Mock]:
@pytest.fixture(scope="function")
def snapshot_download() -> Iterator[mock.Mock]:
with mock.patch("app.models.base.snapshot_download") as mocked:
with mock.patch("immich_ml.models.base.snapshot_download") as mocked:
yield mocked

View File

@ -1 +0,0 @@
3.12

View File

@ -1,165 +0,0 @@
import json
import resource
from pathlib import Path
import typer
from tenacity import retry, stop_after_attempt, wait_fixed
from typing_extensions import Annotated
from .exporters.constants import DELETE_PATTERNS, SOURCE_TO_METADATA, ModelSource, ModelTask
from .exporters.onnx import export as onnx_export
from .exporters.rknn import export as rknn_export
app = typer.Typer(pretty_exceptions_show_locals=False)
def generate_readme(model_name: str, model_source: ModelSource) -> str:
(name, link, type) = SOURCE_TO_METADATA[model_source]
match model_source:
case ModelSource.MCLIP:
tags = ["immich", "clip", "multilingual"]
case ModelSource.OPENCLIP:
tags = ["immich", "clip"]
lowered = model_name.lower()
if "xlm" in lowered or "nllb" in lowered:
tags.append("multilingual")
case ModelSource.INSIGHTFACE:
tags = ["immich", "facial-recognition"]
case _:
raise ValueError(f"Unsupported model source {model_source}")
return f"""---
tags:
{" - " + "\n - ".join(tags)}
---
# Model Description
This repo contains ONNX exports for the associated {type} model by {name}. See the [{name}]({link}) repo for more info.
This repo is specifically intended for use with [Immich](https://immich.app/), a self-hosted photo library.
"""
def clean_name(model_name: str) -> str:
hf_model_name = model_name.split("/")[-1]
hf_model_name = hf_model_name.replace("xlm-roberta-large", "XLM-Roberta-Large")
hf_model_name = hf_model_name.replace("xlm-roberta-base", "XLM-Roberta-Base")
return hf_model_name
@app.command()
def export(model_name: str, model_source: ModelSource, output_dir: Path = Path("models"), cache: bool = True) -> None:
hf_model_name = clean_name(model_name)
output_dir = output_dir / hf_model_name
match model_source:
case ModelSource.MCLIP | ModelSource.OPENCLIP:
output_dir.mkdir(parents=True, exist_ok=True)
onnx_export(model_name, model_source, output_dir, cache=cache)
case ModelSource.INSIGHTFACE:
from huggingface_hub import snapshot_download
# TODO: start from insightface dump instead of downloading from HF
snapshot_download(f"immich-app/{hf_model_name}", local_dir=output_dir)
case _:
raise ValueError(f"Unsupported model source {model_source}")
try:
rknn_export(output_dir, cache=cache)
except Exception as e:
print(f"Failed to export model {model_name} to rknn: {e}")
(output_dir / "rknpu").unlink(missing_ok=True)
readme_path = output_dir / "README.md"
if not (cache or readme_path.exists()):
with open(readme_path, "w") as f:
f.write(generate_readme(model_name, model_source))
@app.command()
def profile(model_dir: Path, model_task: ModelTask, output_path: Path) -> None:
from timeit import timeit
import numpy as np
import onnxruntime as ort
np.random.seed(0)
sess_options = ort.SessionOptions()
sess_options.enable_cpu_mem_arena = False
providers = ["CPUExecutionProvider"]
provider_options = [{"arena_extend_strategy": "kSameAsRequested"}]
match model_task:
case ModelTask.SEARCH:
textual = ort.InferenceSession(
model_dir / "textual" / "model.onnx",
sess_options=sess_options,
providers=providers,
provider_options=provider_options,
)
tokens = {node.name: np.random.rand(*node.shape).astype(np.int32) for node in textual.get_inputs()}
visual = ort.InferenceSession(
model_dir / "visual" / "model.onnx",
sess_options=sess_options,
providers=providers,
provider_options=provider_options,
)
image = {node.name: np.random.rand(*node.shape).astype(np.float32) for node in visual.get_inputs()}
def predict() -> None:
textual.run(None, tokens)
visual.run(None, image)
case ModelTask.FACIAL_RECOGNITION:
detection = ort.InferenceSession(
model_dir / "detection" / "model.onnx",
sess_options=sess_options,
providers=providers,
provider_options=provider_options,
)
image = {node.name: np.random.rand(1, 3, 640, 640).astype(np.float32) for node in detection.get_inputs()}
recognition = ort.InferenceSession(
model_dir / "recognition" / "model.onnx",
sess_options=sess_options,
providers=providers,
provider_options=provider_options,
)
face = {node.name: np.random.rand(1, 3, 112, 112).astype(np.float32) for node in recognition.get_inputs()}
def predict() -> None:
detection.run(None, image)
recognition.run(None, face)
case _:
raise ValueError(f"Unsupported model task {model_task}")
predict()
ms = timeit(predict, number=100)
rss = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
json.dump({"pretrained_model": model_dir.name, "peak_rss": rss, "exec_time_ms": ms}, output_path.open("w"))
print(f"Model {model_dir.name} took {ms:.2f}ms per iteration using {rss / 1024:.2f}MiB of memory")
@app.command()
def upload(
model_dir: Path,
hf_organization: str = "immich-app",
hf_auth_token: Annotated[str | None, typer.Option(envvar="HF_AUTH_TOKEN")] = None,
) -> None:
from huggingface_hub import create_repo, upload_folder
repo_id = f"{hf_organization}/{model_dir.name}"
@retry(stop=stop_after_attempt(5), wait=wait_fixed(5))
def upload_model() -> None:
create_repo(repo_id, exist_ok=True, token=hf_auth_token)
upload_folder(
repo_id=repo_id,
folder_path=model_dir,
# remote repo files to be deleted before uploading
# deletion is in the same commit as the upload, so it's atomic
delete_patterns=DELETE_PATTERNS,
token=hf_auth_token,
)
upload_model()

View File

@ -1,3 +0,0 @@
from immich_model_exporter import app
app()

View File

@ -1,54 +0,0 @@
from enum import StrEnum
from typing import NamedTuple
class ModelSource(StrEnum):
INSIGHTFACE = "insightface"
MCLIP = "mclip"
OPENCLIP = "openclip"
class ModelTask(StrEnum):
FACIAL_RECOGNITION = "facial-recognition"
SEARCH = "clip"
class SourceMetadata(NamedTuple):
name: str
link: str
type: str
SOURCE_TO_METADATA = {
ModelSource.MCLIP: SourceMetadata("M-CLIP", "https://huggingface.co/M-CLIP", "CLIP"),
ModelSource.OPENCLIP: SourceMetadata("OpenCLIP", "https://github.com/mlfoundations/open_clip", "CLIP"),
ModelSource.INSIGHTFACE: SourceMetadata(
"InsightFace", "https://github.com/deepinsight/insightface/tree/master", "facial recognition"
),
}
SOURCE_TO_TASK = {
ModelSource.MCLIP: ModelTask.SEARCH,
ModelSource.OPENCLIP: ModelTask.SEARCH,
ModelSource.INSIGHTFACE: ModelTask.FACIAL_RECOGNITION,
}
RKNN_SOCS = ["rk3566", "rk3568", "rk3576", "rk3588"]
# glob to delete old UUID blobs when reuploading models
_uuid_char = "[a-fA-F0-9]"
_uuid_glob = _uuid_char * 8 + "-" + _uuid_char * 4 + "-" + _uuid_char * 4 + "-" + _uuid_char * 4 + "-" + _uuid_char * 12
DELETE_PATTERNS = [
"**/*onnx*",
"**/Constant*",
"**/*.weight",
"**/*.bias",
"**/*.proj",
"**/*in_proj_bias",
"**/*.npy",
"**/*.latent",
"**/*.pos_embed",
f"**/{_uuid_glob}",
]

View File

@ -1,20 +0,0 @@
from pathlib import Path
from ..constants import ModelSource
from .models import mclip, openclip
def export(
model_name: str, model_source: ModelSource, output_dir: Path, opset_version: int = 19, cache: bool = True
) -> None:
visual_dir = output_dir / "visual"
textual_dir = output_dir / "textual"
match model_source:
case ModelSource.MCLIP:
mclip.to_onnx(model_name, opset_version, visual_dir, textual_dir, cache=cache)
case ModelSource.OPENCLIP:
name, _, pretrained = model_name.partition("__")
config = openclip.OpenCLIPModelConfig(name, pretrained)
openclip.to_onnx(config, opset_version, visual_dir, textual_dir, cache=cache)
case _:
raise ValueError(f"Unsupported model source {model_source}")

View File

@ -1,77 +0,0 @@
import warnings
from pathlib import Path
from typing import Any
from .openclip import OpenCLIPModelConfig
from .openclip import to_onnx as openclip_to_onnx
from .util import get_model_path
_MCLIP_TO_OPENCLIP = {
"M-CLIP/XLM-Roberta-Large-Vit-B-32": OpenCLIPModelConfig("ViT-B-32", "openai"),
"M-CLIP/XLM-Roberta-Large-Vit-B-16Plus": OpenCLIPModelConfig("ViT-B-16-plus-240", "laion400m_e32"),
"M-CLIP/LABSE-Vit-L-14": OpenCLIPModelConfig("ViT-L-14", "openai"),
"M-CLIP/XLM-Roberta-Large-Vit-L-14": OpenCLIPModelConfig("ViT-L-14", "openai"),
}
def to_onnx(
model_name: str,
opset_version: int,
output_dir_visual: Path | str,
output_dir_textual: Path | str,
cache: bool = True,
) -> tuple[Path, Path]:
textual_path = get_model_path(output_dir_textual)
if not cache or not textual_path.exists():
import torch
from multilingual_clip.pt_multilingual_clip import MultilingualCLIP
from transformers import AutoTokenizer
torch.backends.mha.set_fastpath_enabled(False)
model = MultilingualCLIP.from_pretrained(model_name)
AutoTokenizer.from_pretrained(model_name).save_pretrained(output_dir_textual)
model.eval()
for param in model.parameters():
param.requires_grad_(False)
_export_text_encoder(model, textual_path, opset_version)
else:
print(f"Model {textual_path} already exists, skipping")
visual_path, _ = openclip_to_onnx(_MCLIP_TO_OPENCLIP[model_name], opset_version, output_dir_visual, cache=cache)
assert visual_path is not None, "Visual model export failed"
return visual_path, textual_path
def _export_text_encoder(model: Any, output_path: Path | str, opset_version: int) -> None:
import torch
from multilingual_clip.pt_multilingual_clip import MultilingualCLIP
output_path = Path(output_path)
def forward(self: MultilingualCLIP, input_ids: torch.Tensor, attention_mask: torch.Tensor) -> torch.Tensor:
embs = self.transformer(input_ids, attention_mask)[0]
embs = (embs * attention_mask.unsqueeze(2)).sum(dim=1) / attention_mask.sum(dim=1)[:, None]
embs = self.LinearTransformation(embs)
return torch.nn.functional.normalize(embs, dim=-1)
# unfortunately need to monkeypatch for tracing to work here
# otherwise it hits the 2GiB protobuf serialization limit
MultilingualCLIP.forward = forward
args = (torch.ones(1, 77, dtype=torch.int32), torch.ones(1, 77, dtype=torch.int32))
with warnings.catch_warnings():
warnings.simplefilter("ignore", UserWarning)
torch.onnx.export(
model,
args,
output_path.as_posix(),
input_names=["input_ids", "attention_mask"],
output_names=["embedding"],
opset_version=opset_version,
# dynamic_axes={
# "input_ids": {0: "batch_size", 1: "sequence_length"},
# "attention_mask": {0: "batch_size", 1: "sequence_length"},
# },
)

View File

@ -1,151 +0,0 @@
import warnings
from dataclasses import dataclass
from functools import cached_property
from pathlib import Path
from typing import Any
from .util import get_model_path, save_config
@dataclass
class OpenCLIPModelConfig:
name: str
pretrained: str
@cached_property
def model_config(self) -> dict[str, Any]:
import open_clip
config: dict[str, Any] | None = open_clip.get_model_config(self.name)
if config is None:
raise ValueError(f"Unknown model {self.name}")
return config
@property
def image_size(self) -> int:
image_size: int = self.model_config["vision_cfg"]["image_size"]
return image_size
@property
def sequence_length(self) -> int:
context_length: int = self.model_config["text_cfg"].get("context_length", 77)
return context_length
def to_onnx(
model_cfg: OpenCLIPModelConfig,
opset_version: int,
output_dir_visual: Path | str | None = None,
output_dir_textual: Path | str | None = None,
cache: bool = True,
) -> tuple[Path | None, Path | None]:
visual_path = None
textual_path = None
if output_dir_visual is not None:
output_dir_visual = Path(output_dir_visual)
visual_path = get_model_path(output_dir_visual)
if output_dir_textual is not None:
output_dir_textual = Path(output_dir_textual)
textual_path = get_model_path(output_dir_textual)
if cache and ((textual_path is None or textual_path.exists()) and (visual_path is None or visual_path.exists())):
print(f"Models {textual_path} and {visual_path} already exist, skipping")
return visual_path, textual_path
import open_clip
import torch
from transformers import AutoTokenizer
torch.backends.mha.set_fastpath_enabled(False)
model = open_clip.create_model(
model_cfg.name,
pretrained=model_cfg.pretrained,
jit=False,
require_pretrained=True,
)
text_vision_cfg = open_clip.get_model_config(model_cfg.name)
model.eval()
for param in model.parameters():
param.requires_grad_(False)
if visual_path is not None and output_dir_visual is not None:
if not cache or not visual_path.exists():
save_config(
open_clip.get_model_preprocess_cfg(model),
output_dir_visual / "preprocess_cfg.json",
)
save_config(text_vision_cfg, output_dir_visual.parent / "config.json")
_export_image_encoder(model, model_cfg, visual_path, opset_version)
else:
print(f"Model {visual_path} already exists, skipping")
if textual_path is not None and output_dir_textual is not None:
if not cache or not textual_path.exists():
tokenizer_name = text_vision_cfg["text_cfg"].get("hf_tokenizer_name", "openai/clip-vit-base-patch32")
AutoTokenizer.from_pretrained(tokenizer_name).save_pretrained(output_dir_textual)
_export_text_encoder(model, model_cfg, textual_path, opset_version)
else:
print(f"Model {textual_path} already exists, skipping")
return visual_path, textual_path
def _export_image_encoder(
model: Any, model_cfg: OpenCLIPModelConfig, output_path: Path | str, opset_version: int
) -> None:
import torch
output_path = Path(output_path)
def encode_image(image: torch.Tensor) -> torch.Tensor:
output = model.encode_image(image, normalize=True)
assert isinstance(output, torch.Tensor)
return output
model.forward = encode_image
args = (torch.randn(1, 3, model_cfg.image_size, model_cfg.image_size),)
with warnings.catch_warnings():
warnings.simplefilter("ignore", UserWarning)
torch.onnx.export(
model,
args,
output_path.as_posix(),
input_names=["image"],
output_names=["embedding"],
opset_version=opset_version,
# dynamic_axes={"image": {0: "batch_size"}},
)
def _export_text_encoder(
model: Any, model_cfg: OpenCLIPModelConfig, output_path: Path | str, opset_version: int
) -> None:
import torch
output_path = Path(output_path)
def encode_text(text: torch.Tensor) -> torch.Tensor:
output = model.encode_text(text, normalize=True)
assert isinstance(output, torch.Tensor)
return output
model.forward = encode_text
args = (torch.ones(1, model_cfg.sequence_length, dtype=torch.int32),)
with warnings.catch_warnings():
warnings.simplefilter("ignore", UserWarning)
torch.onnx.export(
model,
args,
output_path.as_posix(),
input_names=["text"],
output_names=["embedding"],
opset_version=opset_version,
# dynamic_axes={"text": {0: "batch_size"}},
)

View File

@ -1,15 +0,0 @@
import json
from pathlib import Path
from typing import Any
def get_model_path(output_dir: Path | str) -> Path:
output_dir = Path(output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
return output_dir / "model.onnx"
def save_config(config: Any, output_path: Path | str) -> None:
output_path = Path(output_path)
output_path.parent.mkdir(parents=True, exist_ok=True)
json.dump(config, output_path.open("w"))

View File

@ -1,96 +0,0 @@
from pathlib import Path
from .constants import RKNN_SOCS
def _export_platform(
model_dir: Path,
target_platform: str,
inputs: list[str] | None = None,
input_size_list: list[list[int]] | None = None,
fuse_matmul_softmax_matmul_to_sdpa: bool = True,
cache: bool = True,
) -> None:
from rknn.api import RKNN
input_path = model_dir / "model.onnx"
output_path = model_dir / "rknpu" / target_platform / "model.rknn"
if cache and output_path.exists():
print(f"Model {input_path} already exists at {output_path}, skipping")
return
print(f"Exporting model {input_path} to {output_path}")
rknn = RKNN(verbose=False)
rknn.config(
target_platform=target_platform,
disable_rules=["fuse_matmul_softmax_matmul_to_sdpa"] if not fuse_matmul_softmax_matmul_to_sdpa else [],
enable_flash_attention=False,
model_pruning=True,
)
ret = rknn.load_onnx(model=input_path.as_posix(), inputs=inputs, input_size_list=input_size_list)
if ret != 0:
raise RuntimeError("Load failed!")
ret = rknn.build(do_quantization=False)
if ret != 0:
raise RuntimeError("Build failed!")
output_path.parent.mkdir(parents=True, exist_ok=True)
ret = rknn.export_rknn(output_path.as_posix())
if ret != 0:
raise RuntimeError("Export rknn model failed!")
def _export_platforms(
model_dir: Path,
inputs: list[str] | None = None,
input_size_list: list[list[int]] | None = None,
cache: bool = True,
) -> None:
fuse_matmul_softmax_matmul_to_sdpa = True
for soc in RKNN_SOCS:
try:
_export_platform(
model_dir,
soc,
inputs=inputs,
input_size_list=input_size_list,
fuse_matmul_softmax_matmul_to_sdpa=fuse_matmul_softmax_matmul_to_sdpa,
cache=cache,
)
except Exception as e:
print(f"Failed to export model for {soc}: {e}")
if "inputs or 'outputs' must be set" in str(e):
print("Retrying without fuse_matmul_softmax_matmul_to_sdpa")
fuse_matmul_softmax_matmul_to_sdpa = False
_export_platform(
model_dir,
soc,
inputs=inputs,
input_size_list=input_size_list,
fuse_matmul_softmax_matmul_to_sdpa=fuse_matmul_softmax_matmul_to_sdpa,
cache=cache,
)
def export(model_dir: Path, cache: bool = True) -> None:
textual = model_dir / "textual"
visual = model_dir / "visual"
detection = model_dir / "detection"
recognition = model_dir / "recognition"
if textual.is_dir():
_export_platforms(textual, cache=cache)
if visual.is_dir():
_export_platforms(visual, cache=cache)
if detection.is_dir():
_export_platforms(detection, inputs=["input.1"], input_size_list=[[1, 3, 640, 640]], cache=cache)
if recognition.is_dir():
_export_platforms(recognition, inputs=["input.1"], input_size_list=[[1, 3, 112, 112]], cache=cache)

View File

@ -1,22 +0,0 @@
import json
from pathlib import Path
models_dir = Path("models")
model_to_embed_dim = {}
for model_dir in models_dir.iterdir():
if not model_dir.is_dir():
continue
config_path = model_dir / "config.json"
if not config_path.exists():
print(f"Skipping {model_dir.name} as it does not have a config.json")
continue
with open(config_path) as f:
config = json.load(f)
embed_dim = config.get("embed_dim")
if embed_dim is None:
print(f"Skipping {model_dir.name} as it does not have an embed_dim")
continue
print(f"{model_dir.name}: {embed_dim}")
model_to_embed_dim[model_dir.name] = {"dimSize": embed_dim}
print(json.dumps(model_to_embed_dim))

View File

@ -1,121 +0,0 @@
import polars as pl
def collapsed_table(language: str, df: pl.DataFrame) -> str:
with pl.Config(
tbl_formatting="ASCII_MARKDOWN",
tbl_hide_column_data_types=True,
tbl_hide_dataframe_shape=True,
fmt_str_lengths=100,
tbl_rows=1000,
tbl_width_chars=1000,
):
return f"<details>\n<summary>{language}</summary>\n{str(df)}\n</details>"
languages = {
"en": "English",
"ar": "Arabic",
"bn": "Bengali",
"zh": "Chinese (Simplified)",
"hr": "Croatian",
"quz": "Cusco Quechua",
"cs": "Czech",
"da": "Danish",
"nl": "Dutch",
"fil": "Filipino",
"fi": "Finnish",
"fr": "French",
"de": "German",
"el": "Greek",
"he": "Hebrew",
"hi": "Hindi",
"hu": "Hungarian",
"id": "Indonesian",
"it": "Italian",
"ja": "Japanese",
"ko": "Korean",
"mi": "Maori",
"no": "Norwegian",
"fa": "Persian",
"pl": "Polish",
"pt": "Portuguese",
"ro": "Romanian",
"ru": "Russian",
"es": "Spanish",
"sw": "Swahili",
"sv": "Swedish",
"te": "Telugu",
"th": "Thai",
"tr": "Turkish",
"uk": "Ukrainian",
"vi": "Vietnamese",
}
profile_df = pl.scan_ndjson("profiling/*.json").select("pretrained_model", "peak_rss", "exec_time_ms")
eval_df = pl.scan_ndjson("results/*.json").select("model", "pretrained", "language", "metrics")
eval_df = eval_df.with_columns(
model=pl.col("model")
.str.replace("xlm-roberta-base", "XLM-Roberta-Base")
.str.replace("xlm-roberta-large", "XLM-Roberta-Large")
)
eval_df = eval_df.with_columns(pretrained_model=pl.concat_str(pl.col("model"), pl.col("pretrained"), separator="__"))
eval_df = eval_df.drop("model", "pretrained")
eval_df = eval_df.join(profile_df, on="pretrained_model")
eval_df = eval_df.with_columns(
recall=(
pl.col("metrics").struct.field("image_retrieval_recall@1")
+ pl.col("metrics").struct.field("image_retrieval_recall@5")
+ pl.col("metrics").struct.field("image_retrieval_recall@10")
)
* (100 / 3)
)
pareto_front = eval_df.join_where(
eval_df.select("language", "peak_rss", "exec_time_ms", "recall").rename(
{
"language": "language_other",
"peak_rss": "peak_rss_other",
"exec_time_ms": "exec_time_ms_other",
"recall": "recall_other",
}
),
(pl.col("language") == pl.col("language_other"))
& (pl.col("peak_rss_other") <= pl.col("peak_rss"))
& (pl.col("exec_time_ms_other") <= pl.col("exec_time_ms"))
& (pl.col("recall_other") >= pl.col("recall"))
& (
(pl.col("peak_rss_other") < pl.col("peak_rss"))
| (pl.col("exec_time_ms_other") < pl.col("exec_time_ms"))
| (pl.col("recall_other") > pl.col("recall"))
),
)
eval_df = eval_df.join(pareto_front, on=["pretrained_model", "language"], how="left")
eval_df = eval_df.with_columns(is_pareto=pl.col("recall_other").is_null())
eval_df = (
eval_df.drop("peak_rss_other", "exec_time_ms_other", "recall_other", "language_other")
.unique(subset=["pretrained_model", "language"])
.collect()
)
eval_df.write_parquet("model_info.parquet")
eval_df = eval_df.drop("metrics")
eval_df = eval_df.filter(pl.col("recall") >= 20)
eval_df = eval_df.sort("recall", descending=True)
eval_df = eval_df.select(
pl.col("pretrained_model").alias("Model"),
(pl.col("peak_rss") / 1024).round().cast(pl.UInt32).alias("Memory (MiB)"),
pl.col("exec_time_ms").round(2).alias("Execution Time (ms)"),
pl.col("language").alias("Language"),
pl.col("recall").round(2).alias("Recall (%)"),
pl.when(pl.col("is_pareto")).then(pl.lit("")).otherwise(pl.lit("")).alias("Pareto Optimal"),
)
for language in languages:
lang_df = eval_df.filter(pl.col("Language") == language).drop("Language")
if lang_df.shape[0] == 0:
continue
print(collapsed_table(languages[language], lang_df))

View File

@ -1,171 +0,0 @@
import subprocess
from pathlib import Path
from exporters.constants import ModelSource
from immich_model_exporter import clean_name
from immich_model_exporter.exporters.constants import SOURCE_TO_TASK
mclip = [
"M-CLIP/LABSE-Vit-L-14",
"M-CLIP/XLM-Roberta-Large-Vit-B-16Plus",
"M-CLIP/XLM-Roberta-Large-Vit-B-32",
"M-CLIP/XLM-Roberta-Large-Vit-L-14",
]
openclip = [
"RN101__openai",
"RN101__yfcc15m",
"RN50__cc12m",
"RN50__openai",
"RN50__yfcc15m",
"RN50x16__openai",
"RN50x4__openai",
"RN50x64__openai",
"ViT-B-16-SigLIP-256__webli",
"ViT-B-16-SigLIP-384__webli",
"ViT-B-16-SigLIP-512__webli",
"ViT-B-16-SigLIP-i18n-256__webli",
"ViT-B-16-SigLIP2__webli",
"ViT-B-16-SigLIP__webli",
"ViT-B-16-plus-240__laion400m_e31",
"ViT-B-16-plus-240__laion400m_e32",
"ViT-B-16__laion400m_e31",
"ViT-B-16__laion400m_e32",
"ViT-B-16__openai",
"ViT-B-32-SigLIP2-256__webli",
"ViT-B-32__laion2b-s34b-b79k",
"ViT-B-32__laion2b_e16",
"ViT-B-32__laion400m_e31",
"ViT-B-32__laion400m_e32",
"ViT-B-32__openai",
"ViT-H-14-378-quickgelu__dfn5b",
"ViT-H-14-quickgelu__dfn5b",
"ViT-H-14__laion2b-s32b-b79k",
"ViT-L-14-336__openai",
"ViT-L-14-quickgelu__dfn2b",
"ViT-L-14__laion2b-s32b-b82k",
"ViT-L-14__laion400m_e31",
"ViT-L-14__laion400m_e32",
"ViT-L-14__openai",
"ViT-L-16-SigLIP-256__webli",
"ViT-L-16-SigLIP-384__webli",
"ViT-L-16-SigLIP2-256__webli",
"ViT-L-16-SigLIP2-384__webli",
"ViT-L-16-SigLIP2-512__webli",
"ViT-SO400M-14-SigLIP-384__webli",
"ViT-SO400M-14-SigLIP2-378__webli",
"ViT-SO400M-14-SigLIP2__webli",
"ViT-SO400M-16-SigLIP2-256__webli",
"ViT-SO400M-16-SigLIP2-384__webli",
"ViT-SO400M-16-SigLIP2-512__webli",
"ViT-gopt-16-SigLIP2-256__webli",
"ViT-gopt-16-SigLIP2-384__webli",
"nllb-clip-base-siglip__mrl",
"nllb-clip-base-siglip__v1",
"nllb-clip-large-siglip__mrl",
"nllb-clip-large-siglip__v1",
"xlm-roberta-base-ViT-B-32__laion5b_s13b_b90k",
"xlm-roberta-large-ViT-H-14__frozen_laion5b_s13b_b90k",
]
insightface = [
"antelopev2",
"buffalo_l",
"buffalo_m",
"buffalo_s",
]
def export_models(models: list[str], source: ModelSource) -> None:
profiling_dir = Path("profiling")
profiling_dir.mkdir(exist_ok=True)
for model in models:
try:
model_dir = f"models/{clean_name(model)}"
task = SOURCE_TO_TASK[source]
print(f"Processing model {model}")
subprocess.check_call(["python", "-m", "immich_model_exporter", "export", model, source])
subprocess.check_call(
[
"python",
"-m",
"immich_model_exporter",
"profile",
model_dir,
task,
"--output_path",
profiling_dir / f"{model}.json",
]
)
subprocess.check_call(["python", "-m", "immich_model_exporter", "upload", model_dir])
except Exception as e:
print(f"Failed to export model {model}: {e}")
if __name__ == "__main__":
export_models(mclip, ModelSource.MCLIP)
export_models(openclip, ModelSource.OPENCLIP)
export_models(insightface, ModelSource.INSIGHTFACE)
Path("results").mkdir(exist_ok=True)
subprocess.check_call(
[
"python",
"clip_benchmark",
"eval",
"--pretrained_model",
*[name.replace("__", ",") for name in openclip],
"--task",
"zeroshot_retrieval",
"--dataset",
"crossmodal3600",
"--batch_size",
"64",
"--language",
"ar",
"bn",
"cs",
"da",
"de",
"el",
"en",
"es",
"fa",
"fi",
"fil",
"fr",
"he",
"hi",
"hr",
"hu",
"id",
"it",
"ja",
"ko",
"mi",
"nl",
"no",
"pl",
"pt",
"quz",
"ro",
"ru",
"sv",
"sw",
"te",
"th",
"tr",
"uk",
"vi",
"zh",
"--recall_k",
"1",
"5",
"10",
"--no_amp",
"--output",
"results/{dataset}_{language}_{model}_{pretrained}.json",
]
)

View File

@ -1,60 +0,0 @@
[project]
name = "immich_model_exporter"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.10, <4.0"
dependencies = [
"huggingface-hub>=0.29.3",
"multilingual-clip>=1.0.10",
"onnx>=1.14.1",
"onnxruntime>=1.16.0",
"open-clip-torch>=2.31.0",
"typer>=0.15.2",
"rknn-toolkit2>=2.3.0",
"transformers>=4.49.0",
"tenacity>=9.0.0",
"clip-benchmark>=1.6.1",
"polars>=1.25.2",
]
[dependency-groups]
dev = ["black>=23.3.0", "mypy>=1.3.0", "ruff>=0.0.272"]
[tool.uv]
override-dependencies = [
"onnx>=1.16.0,<2",
"onnxruntime>=1.18.2,<2",
"torch>=2.4",
"torchvision>=0.21",
]
[tool.hatch.build.targets.sdist]
include = ["immich_model_exporter"]
[tool.hatch.build.targets.wheel]
include = ["immich_model_exporter"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.mypy]
python_version = "3.12"
follow_imports = "silent"
warn_redundant_casts = true
disallow_any_generics = true
check_untyped_defs = true
disallow_untyped_defs = true
ignore_missing_imports = true
[tool.ruff]
line-length = 120
target-version = "py312"
[tool.ruff.lint]
select = ["E", "F", "I"]
[tool.black]
line-length = 120
target-version = ['py312']

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,43 @@
import os
import signal
import subprocess
from pathlib import Path
from .config import log, non_prefixed_settings, settings
if source_ref := os.getenv("IMMICH_SOURCE_REF"):
log.info(f"Initializing Immich ML [{source_ref}]")
else:
log.info("Initializing Immich ML")
module_dir = Path(__file__).parent
try:
with subprocess.Popen(
[
"python",
"-m",
"gunicorn",
"immich_ml.main:app",
"-k",
"immich_ml.config.CustomUvicornWorker",
"-c",
module_dir / "gunicorn_conf.py",
"-b",
f"{non_prefixed_settings.immich_host}:{non_prefixed_settings.immich_port}",
"-w",
str(settings.workers),
"-t",
str(settings.worker_timeout),
"--log-config-json",
module_dir / "log_conf.json",
"--keep-alive",
str(settings.http_keepalive_timeout_s),
"--graceful-timeout",
"10",
],
) as cmd:
cmd.wait()
except KeyboardInterrupt:
cmd.send_signal(signal.SIGINT)
exit(cmd.returncode)

View File

@ -51,12 +51,12 @@ class Settings(BaseSettings):
protected_namespaces=("settings_",),
)
cache_folder: Path = Path("/cache")
cache_folder: Path = (Path.home() / ".cache" / "immich_ml").resolve()
model_ttl: int = 300
model_ttl_poll_s: int = 10
host: str = "0.0.0.0"
port: int = 3003
workers: int = 1
worker_timeout: int = 300
http_keepalive_timeout_s: int = 2
test_full: bool = False
request_threads: int = os.cpu_count() or 4
model_inter_op_threads: int = 0
@ -74,9 +74,11 @@ class Settings(BaseSettings):
return os.environ.get("MACHINE_LEARNING_DEVICE_ID", "0")
class LogSettings(BaseSettings):
class NonPrefixedSettings(BaseSettings):
model_config = SettingsConfigDict(case_sensitive=False)
immich_host: str = "[::]"
immich_port: int = 3003
immich_log_level: str = "info"
no_color: bool = False
@ -100,14 +102,14 @@ LOG_LEVELS: dict[str, int] = {
}
settings = Settings()
log_settings = LogSettings()
non_prefixed_settings = NonPrefixedSettings()
LOG_LEVEL = LOG_LEVELS.get(log_settings.immich_log_level.lower(), logging.INFO)
LOG_LEVEL = LOG_LEVELS.get(non_prefixed_settings.immich_log_level.lower(), logging.INFO)
class CustomRichHandler(RichHandler):
def __init__(self) -> None:
console = Console(color_system="standard", no_color=log_settings.no_color)
console = Console(color_system="standard", no_color=non_prefixed_settings.no_color)
self.excluded = ["uvicorn", "starlette", "fastapi"]
super().__init__(
show_path=False,

View File

@ -0,0 +1,21 @@
{
"version": 1,
"disable_existing_loggers": false,
"handlers": {
"console": {
"class": "immich_ml.config.CustomRichHandler"
}
},
"loggers": {
"gunicorn.error": {
"handlers": [
"console"
]
}
},
"root": {
"handlers": [
"console"
]
}
}

View File

@ -18,9 +18,9 @@ from PIL.Image import Image
from pydantic import ValidationError
from starlette.formparsers import MultiPartParser
from app.models import get_model_deps
from app.models.base import InferenceModel
from app.models.transforms import decode_pil
from immich_ml.models import get_model_deps
from immich_ml.models.base import InferenceModel
from immich_ml.models.transforms import decode_pil
from .config import PreloadModelData, log, settings
from .models.cache import ModelCache

View File

@ -1,9 +1,9 @@
from typing import Any
from app.models.base import InferenceModel
from app.models.clip.textual import MClipTextualEncoder, OpenClipTextualEncoder
from app.models.clip.visual import OpenClipVisualEncoder
from app.schemas import ModelSource, ModelTask, ModelType
from immich_ml.models.base import InferenceModel
from immich_ml.models.clip.textual import MClipTextualEncoder, OpenClipTextualEncoder
from immich_ml.models.clip.visual import OpenClipVisualEncoder
from immich_ml.schemas import ModelSource, ModelTask, ModelType
from .constants import get_model_source
from .facial_recognition.detection import FaceDetector

View File

@ -7,9 +7,9 @@ from typing import Any, ClassVar
from huggingface_hub import snapshot_download
import ann.ann
import app.sessions.rknn as rknn
from app.sessions.ort import OrtSession
import immich_ml.sessions.ann.loader
import immich_ml.sessions.rknn as rknn
from immich_ml.sessions.ort import OrtSession
from ..config import clean_name, log, settings
from ..schemas import ModelFormat, ModelIdentity, ModelSession, ModelTask, ModelType
@ -171,7 +171,7 @@ class InferenceModel(ABC):
def _model_format_default(self) -> ModelFormat:
if rknn.is_available:
return ModelFormat.RKNN
elif ann.ann.is_available and settings.ann:
elif immich_ml.sessions.ann.loader.is_available and settings.ann:
return ModelFormat.ARMNN
else:
return ModelFormat.ONNX

View File

@ -4,8 +4,8 @@ from aiocache.backends.memory import SimpleMemoryCache
from aiocache.lock import OptimisticLock
from aiocache.plugins import TimingPlugin
from app.models import from_model_type
from app.models.base import InferenceModel
from immich_ml.models import from_model_type
from immich_ml.models.base import InferenceModel
from ..schemas import ModelTask, ModelType, has_profiling

View File

@ -8,10 +8,10 @@ import numpy as np
from numpy.typing import NDArray
from tokenizers import Encoding, Tokenizer
from app.config import log
from app.models.base import InferenceModel
from app.models.transforms import clean_text, serialize_np_array
from app.schemas import ModelSession, ModelTask, ModelType
from immich_ml.config import log
from immich_ml.models.base import InferenceModel
from immich_ml.models.transforms import clean_text, serialize_np_array
from immich_ml.schemas import ModelSession, ModelTask, ModelType
class BaseCLIPTextualEncoder(InferenceModel):

View File

@ -8,9 +8,9 @@ import numpy as np
from numpy.typing import NDArray
from PIL import Image
from app.config import log
from app.models.base import InferenceModel
from app.models.transforms import (
from immich_ml.config import log
from immich_ml.models.base import InferenceModel
from immich_ml.models.transforms import (
crop_pil,
decode_pil,
get_pil_resampling,
@ -19,7 +19,7 @@ from app.models.transforms import (
serialize_np_array,
to_numpy,
)
from app.schemas import ModelSession, ModelTask, ModelType
from immich_ml.schemas import ModelSession, ModelTask, ModelType
class BaseCLIPVisualEncoder(InferenceModel):

View File

@ -1,5 +1,5 @@
from app.config import clean_name
from app.schemas import ModelSource
from immich_ml.config import clean_name
from immich_ml.schemas import ModelSource
_OPENCLIP_MODELS = {
"RN101__openai",

View File

@ -4,9 +4,9 @@ import numpy as np
from insightface.model_zoo import RetinaFace
from numpy.typing import NDArray
from app.models.base import InferenceModel
from app.models.transforms import decode_cv2
from app.schemas import FaceDetectionOutput, ModelSession, ModelTask, ModelType
from immich_ml.models.base import InferenceModel
from immich_ml.models.transforms import decode_cv2
from immich_ml.schemas import FaceDetectionOutput, ModelSession, ModelTask, ModelType
class FaceDetector(InferenceModel):

View File

@ -10,10 +10,17 @@ from numpy.typing import NDArray
from onnx.tools.update_model_dims import update_inputs_outputs_dims
from PIL import Image
from app.config import log, settings
from app.models.base import InferenceModel
from app.models.transforms import decode_cv2, serialize_np_array
from app.schemas import FaceDetectionOutput, FacialRecognitionOutput, ModelFormat, ModelSession, ModelTask, ModelType
from immich_ml.config import log, settings
from immich_ml.models.base import InferenceModel
from immich_ml.models.transforms import decode_cv2, serialize_np_array
from immich_ml.schemas import (
FaceDetectionOutput,
FacialRecognitionOutput,
ModelFormat,
ModelSession,
ModelTask,
ModelType,
)
class FaceRecognizer(InferenceModel):

View File

@ -6,10 +6,10 @@ from typing import Any, NamedTuple
import numpy as np
from numpy.typing import NDArray
from ann.ann import Ann
from app.schemas import SessionNode
from immich_ml.config import log, settings
from immich_ml.schemas import SessionNode
from ..config import log, settings
from .loader import Ann
class AnnSession:

View File

@ -7,7 +7,7 @@ from typing import Any, Protocol, TypeVar
import numpy as np
from numpy.typing import NDArray
from app.config import log
from immich_ml.config import log
try:
CDLL("libmali.so") # fail if libmali.so is not mounted into container

View File

@ -7,8 +7,8 @@ import numpy as np
import onnxruntime as ort
from numpy.typing import NDArray
from app.models.constants import SUPPORTED_PROVIDERS
from app.schemas import SessionNode
from immich_ml.models.constants import SUPPORTED_PROVIDERS
from immich_ml.schemas import SessionNode
from ..config import log, settings

View File

@ -6,8 +6,8 @@ from typing import Any, NamedTuple
import numpy as np
from numpy.typing import NDArray
from app.config import log, settings
from app.schemas import SessionNode
from immich_ml.config import log, settings
from immich_ml.schemas import SessionNode
from .rknnpool import RknnPoolExecutor, is_available, soc_name

View File

@ -10,8 +10,8 @@ from typing import Callable
import numpy as np
from numpy.typing import NDArray
from app.config import log
from app.models.constants import RKNN_COREMASK_SUPPORTED_SOCS, RKNN_SUPPORTED_SOCS
from immich_ml.config import log
from immich_ml.models.constants import RKNN_COREMASK_SUPPORTED_SOCS, RKNN_SUPPORTED_SOCS
def get_soc(device_tree_path: Path | str) -> str | None:

View File

@ -1,15 +0,0 @@
{
"version": 1,
"disable_existing_loggers": false,
"handlers": {
"console": {
"class": "app.config.CustomRichHandler"
}
},
"loggers": {
"gunicorn.error": {
"handlers": ["console"]
}
},
"root": { "handlers": ["console"] }
}

View File

@ -1,5 +1,5 @@
[project]
name = "machine-learning"
name = "immich-ml"
version = "1.129.0"
description = ""
authors = [{ name = "Hau Tran", email = "alex.tran1502@gmail.com" }]
@ -66,10 +66,10 @@ explicit = true
onnxruntime-gpu = { index = "cuda12" }
[tool.hatch.build.targets.sdist]
include = ["app"]
include = ["immich_ml"]
[tool.hatch.build.targets.wheel]
include = ["app"]
include = ["immich_ml"]
[build-system]
requires = ["hatchling"]

View File

@ -1,31 +0,0 @@
#!/usr/bin/env sh
echo "Initializing Immich ML $IMMICH_SOURCE_REF"
if ! [ "$DEVICE" = "openvino" ]; then
: "${MACHINE_LEARNING_WORKER_TIMEOUT:=120}"
else
: "${MACHINE_LEARNING_WORKER_TIMEOUT:=300}"
fi
# mimalloc seems to increase memory usage dramatically with openvino, need to investigate
if ! [ "$DEVICE" = "openvino" ] && ! [ "$DEVICE" = "rocm" ]; then
lib_path="/usr/lib/$(arch)-linux-gnu/libmimalloc.so.2"
export LD_PRELOAD="$lib_path"
export LD_BIND_NOW=1
fi
: "${IMMICH_HOST:=[::]}"
: "${IMMICH_PORT:=3003}"
: "${MACHINE_LEARNING_WORKERS:=1}"
: "${MACHINE_LEARNING_HTTP_KEEPALIVE_TIMEOUT_S:=2}"
gunicorn app.main:app \
-k app.config.CustomUvicornWorker \
-c gunicorn_conf.py \
-b "$IMMICH_HOST":"$IMMICH_PORT" \
-w "$MACHINE_LEARNING_WORKERS" \
-t "$MACHINE_LEARNING_WORKER_TIMEOUT" \
--log-config-json log_conf.json \
--keep-alive "$MACHINE_LEARNING_HTTP_KEEPALIVE_TIMEOUT_S" \
--graceful-timeout 0

View File

@ -18,19 +18,18 @@ from PIL import Image
from pytest import MonkeyPatch
from pytest_mock import MockerFixture
from app.main import load, preload_models
from app.models.clip.textual import MClipTextualEncoder, OpenClipTextualEncoder
from app.models.clip.visual import OpenClipVisualEncoder
from app.models.facial_recognition.detection import FaceDetector
from app.models.facial_recognition.recognition import FaceRecognizer
from app.sessions.ann import AnnSession
from app.sessions.ort import OrtSession
from app.sessions.rknn import RknnSession, run_inference
from .config import Settings, settings
from .models.base import InferenceModel
from .models.cache import ModelCache
from .schemas import ModelFormat, ModelTask, ModelType
from immich_ml.config import Settings, settings
from immich_ml.main import load, preload_models
from immich_ml.models.base import InferenceModel
from immich_ml.models.cache import ModelCache
from immich_ml.models.clip.textual import MClipTextualEncoder, OpenClipTextualEncoder
from immich_ml.models.clip.visual import OpenClipVisualEncoder
from immich_ml.models.facial_recognition.detection import FaceDetector
from immich_ml.models.facial_recognition.recognition import FaceRecognizer
from immich_ml.schemas import ModelFormat, ModelTask, ModelType
from immich_ml.sessions.ann import AnnSession
from immich_ml.sessions.ort import OrtSession
from immich_ml.sessions.rknn import RknnSession, run_inference
class TestBase:
@ -47,7 +46,7 @@ class TestBase:
def test_sets_default_model_format(self, mocker: MockerFixture) -> None:
mocker.patch.object(settings, "ann", True)
mocker.patch("ann.ann.is_available", False)
mocker.patch("immich_ml.sessions.ann.loader.is_available", False)
encoder = OpenClipTextualEncoder("ViT-B-32__openai")
@ -55,7 +54,7 @@ class TestBase:
def test_sets_default_model_format_to_armnn_if_available(self, path: mock.Mock, mocker: MockerFixture) -> None:
mocker.patch.object(settings, "ann", True)
mocker.patch("ann.ann.is_available", True)
mocker.patch("immich_ml.sessions.ann.loader.is_available", True)
path.suffix = ".armnn"
encoder = OpenClipTextualEncoder("ViT-B-32__openai", cache_dir=path)
@ -64,7 +63,7 @@ class TestBase:
def test_sets_model_format_kwarg(self, mocker: MockerFixture) -> None:
mocker.patch.object(settings, "ann", False)
mocker.patch("ann.ann.is_available", False)
mocker.patch("immich_ml.sessions.ann.loader.is_available", False)
encoder = OpenClipTextualEncoder("ViT-B-32__openai", model_format=ModelFormat.ARMNN)
@ -72,7 +71,7 @@ class TestBase:
def test_sets_default_model_format_to_rknn_if_available(self, mocker: MockerFixture) -> None:
mocker.patch.object(settings, "rknn", True)
mocker.patch("app.sessions.rknn.is_available", True)
mocker.patch("immich_ml.sessions.rknn.is_available", True)
encoder = OpenClipTextualEncoder("ViT-B-32__openai")
@ -294,7 +293,7 @@ class TestOrtSession:
assert session.sess_options.intra_op_num_threads == 0
def test_sets_default_sess_options_sets_threads_if_non_cpu_and_set_threads(self, mocker: MockerFixture) -> None:
mock_settings = mocker.patch("app.sessions.ort.settings", autospec=True)
mock_settings = mocker.patch("immich_ml.sessions.ort.settings", autospec=True)
mock_settings.model_inter_op_threads = 2
mock_settings.model_intra_op_threads = 4
@ -373,8 +372,8 @@ class TestRknnSession:
def test_creates_rknn_session(self, rknn_session: mock.Mock, info: mock.Mock, mocker: MockerFixture) -> None:
model_path = mock.MagicMock(spec=Path)
tpe = 1
mocker.patch("app.sessions.rknn.soc_name", "rk3566")
mocker.patch("app.sessions.rknn.is_available", True)
mocker.patch("immich_ml.sessions.rknn.soc_name", "rk3566")
mocker.patch("immich_ml.sessions.rknn.is_available", True)
RknnSession(model_path)
rknn_session.assert_called_once_with(model_path=model_path.as_posix(), tpes=tpe, func=run_inference)
@ -384,7 +383,7 @@ class TestRknnSession:
def test_run_rknn(self, rknn_session: mock.Mock, mocker: MockerFixture) -> None:
rknn_session.return_value.load.return_value = 123
np_spy = mocker.spy(np, "ascontiguousarray")
mocker.patch("app.sessions.rknn.soc_name", "rk3566")
mocker.patch("immich_ml.sessions.rknn.soc_name", "rk3566")
session = RknnSession(Path("ViT-B-32__openai"))
[input1, input2] = [np.random.rand(1, 3, 224, 224).astype(np.float32) for _ in range(2)]
input_feed = {"input.1": input1, "input.2": input2}
@ -434,7 +433,7 @@ class TestCLIP:
mocked = mocker.patch.object(InferenceModel, "_make_session", autospec=True).return_value
mocked.run.return_value = [[self.embedding]]
mocker.patch("app.models.clip.textual.Tokenizer.from_file", autospec=True)
mocker.patch("immich_ml.models.clip.textual.Tokenizer.from_file", autospec=True)
clip_encoder = OpenClipTextualEncoder("ViT-B-32__openai", cache_dir="test_cache")
embedding_str = clip_encoder.predict("test search query")
@ -454,7 +453,7 @@ class TestCLIP:
mocker.patch.object(OpenClipTextualEncoder, "model_cfg", clip_model_cfg)
mocker.patch.object(OpenClipTextualEncoder, "tokenizer_cfg", clip_tokenizer_cfg)
mocker.patch.object(InferenceModel, "_make_session", autospec=True).return_value
mock_tokenizer = mocker.patch("app.models.clip.textual.Tokenizer.from_file", autospec=True).return_value
mock_tokenizer = mocker.patch("immich_ml.models.clip.textual.Tokenizer.from_file", autospec=True).return_value
mock_ids = [randint(0, 50000) for _ in range(77)]
mock_tokenizer.encode.return_value = SimpleNamespace(ids=mock_ids)
@ -480,7 +479,7 @@ class TestCLIP:
mocker.patch.object(OpenClipTextualEncoder, "model_cfg", clip_model_cfg)
mocker.patch.object(OpenClipTextualEncoder, "tokenizer_cfg", clip_tokenizer_cfg)
mocker.patch.object(InferenceModel, "_make_session", autospec=True).return_value
mock_tokenizer = mocker.patch("app.models.clip.textual.Tokenizer.from_file", autospec=True).return_value
mock_tokenizer = mocker.patch("immich_ml.models.clip.textual.Tokenizer.from_file", autospec=True).return_value
mock_ids = [randint(0, 50000) for _ in range(77)]
mock_tokenizer.encode.return_value = SimpleNamespace(ids=mock_ids)
@ -505,7 +504,7 @@ class TestCLIP:
mocker.patch.object(MClipTextualEncoder, "model_cfg", clip_model_cfg)
mocker.patch.object(MClipTextualEncoder, "tokenizer_cfg", clip_tokenizer_cfg)
mocker.patch.object(InferenceModel, "_make_session", autospec=True).return_value
mock_tokenizer = mocker.patch("app.models.clip.textual.Tokenizer.from_file", autospec=True).return_value
mock_tokenizer = mocker.patch("immich_ml.models.clip.textual.Tokenizer.from_file", autospec=True).return_value
mock_ids = [randint(0, 50000) for _ in range(77)]
mock_attention_mask = [randint(0, 1) for _ in range(77)]
mock_tokenizer.encode.return_value = SimpleNamespace(ids=mock_ids, attention_mask=mock_attention_mask)
@ -597,12 +596,12 @@ class TestFaceRecognition:
def test_recognition_adds_batch_axis_for_ort(
self, ort_session: mock.Mock, path: mock.Mock, mocker: MockerFixture
) -> None:
onnx = mocker.patch("app.models.facial_recognition.recognition.onnx", autospec=True)
onnx = mocker.patch("immich_ml.models.facial_recognition.recognition.onnx", autospec=True)
update_dims = mocker.patch(
"app.models.facial_recognition.recognition.update_inputs_outputs_dims", autospec=True
"immich_ml.models.facial_recognition.recognition.update_inputs_outputs_dims", autospec=True
)
mocker.patch("app.models.base.InferenceModel.download")
mocker.patch("app.models.facial_recognition.recognition.ArcFaceONNX")
mocker.patch("immich_ml.models.base.InferenceModel.download")
mocker.patch("immich_ml.models.facial_recognition.recognition.ArcFaceONNX")
ort_session.return_value.get_inputs.return_value = [SimpleNamespace(name="input.1", shape=(1, 3, 224, 224))]
ort_session.return_value.get_outputs.return_value = [SimpleNamespace(name="output.1", shape=(1, 800))]
path.return_value.__truediv__.return_value.__truediv__.return_value.suffix = ".onnx"
@ -631,12 +630,12 @@ class TestFaceRecognition:
def test_recognition_does_not_add_batch_axis_if_exists(
self, ort_session: mock.Mock, path: mock.Mock, mocker: MockerFixture
) -> None:
onnx = mocker.patch("app.models.facial_recognition.recognition.onnx", autospec=True)
onnx = mocker.patch("immich_ml.models.facial_recognition.recognition.onnx", autospec=True)
update_dims = mocker.patch(
"app.models.facial_recognition.recognition.update_inputs_outputs_dims", autospec=True
"immich_ml.models.facial_recognition.recognition.update_inputs_outputs_dims", autospec=True
)
mocker.patch("app.models.base.InferenceModel.download")
mocker.patch("app.models.facial_recognition.recognition.ArcFaceONNX")
mocker.patch("immich_ml.models.base.InferenceModel.download")
mocker.patch("immich_ml.models.facial_recognition.recognition.ArcFaceONNX")
path.return_value.__truediv__.return_value.__truediv__.return_value.suffix = ".onnx"
inputs = [SimpleNamespace(name="input.1", shape=("batch", 3, 224, 224))]
@ -655,12 +654,12 @@ class TestFaceRecognition:
def test_recognition_does_not_add_batch_axis_for_armnn(
self, ann_session: mock.Mock, path: mock.Mock, mocker: MockerFixture
) -> None:
onnx = mocker.patch("app.models.facial_recognition.recognition.onnx", autospec=True)
onnx = mocker.patch("immich_ml.models.facial_recognition.recognition.onnx", autospec=True)
update_dims = mocker.patch(
"app.models.facial_recognition.recognition.update_inputs_outputs_dims", autospec=True
"immich_ml.models.facial_recognition.recognition.update_inputs_outputs_dims", autospec=True
)
mocker.patch("app.models.base.InferenceModel.download")
mocker.patch("app.models.facial_recognition.recognition.ArcFaceONNX")
mocker.patch("immich_ml.models.base.InferenceModel.download")
mocker.patch("immich_ml.models.facial_recognition.recognition.ArcFaceONNX")
path.return_value.__truediv__.return_value.__truediv__.return_value.suffix = ".armnn"
inputs = [SimpleNamespace(name="input.1", shape=("batch", 3, 224, 224))]
@ -679,12 +678,12 @@ class TestFaceRecognition:
def test_recognition_does_not_add_batch_axis_for_openvino(
self, ort_session: mock.Mock, path: mock.Mock, mocker: MockerFixture
) -> None:
onnx = mocker.patch("app.models.facial_recognition.recognition.onnx", autospec=True)
onnx = mocker.patch("immich_ml.models.facial_recognition.recognition.onnx", autospec=True)
update_dims = mocker.patch(
"app.models.facial_recognition.recognition.update_inputs_outputs_dims", autospec=True
"immich_ml.models.facial_recognition.recognition.update_inputs_outputs_dims", autospec=True
)
mocker.patch("app.models.base.InferenceModel.download")
mocker.patch("app.models.facial_recognition.recognition.ArcFaceONNX")
mocker.patch("immich_ml.models.base.InferenceModel.download")
mocker.patch("immich_ml.models.facial_recognition.recognition.ArcFaceONNX")
path.return_value.__truediv__.return_value.__truediv__.return_value.suffix = ".onnx"
inputs = [SimpleNamespace(name="input.1", shape=("batch", 3, 224, 224))]
@ -733,13 +732,13 @@ class TestCache:
)
assert len(model_cache.cache._cache) == 2
@mock.patch("app.models.cache.OptimisticLock", autospec=True)
@mock.patch("immich_ml.models.cache.OptimisticLock", autospec=True)
async def test_model_ttl(self, mock_lock_cls: mock.Mock, mock_get_model: mock.Mock) -> None:
model_cache = ModelCache()
await model_cache.get("test_model_name", ModelType.RECOGNITION, ModelTask.FACIAL_RECOGNITION, ttl=100)
mock_lock_cls.return_value.__aenter__.return_value.cas.assert_called_with(mock.ANY, ttl=100)
@mock.patch("app.models.cache.SimpleMemoryCache.expire")
@mock.patch("immich_ml.models.cache.SimpleMemoryCache.expire")
async def test_revalidate_get(self, mock_cache_expire: mock.Mock, mock_get_model: mock.Mock) -> None:
model_cache = ModelCache(revalidate=True)
await model_cache.get("test_model_name", ModelType.RECOGNITION, ModelTask.FACIAL_RECOGNITION, ttl=100)
@ -784,7 +783,7 @@ class TestCache:
assert settings.preload.clip.visual == "ViT-B-32__openai"
model_cache = ModelCache()
monkeypatch.setattr("app.main.model_cache", model_cache)
monkeypatch.setattr("immich_ml.main.model_cache", model_cache)
await preload_models(settings.preload)
mock_get_model.assert_has_calls(
@ -807,7 +806,7 @@ class TestCache:
assert settings.preload.facial_recognition.recognition == "buffalo_s"
model_cache = ModelCache()
monkeypatch.setattr("app.main.model_cache", model_cache)
monkeypatch.setattr("immich_ml.main.model_cache", model_cache)
await preload_models(settings.preload)
mock_get_model.assert_has_calls(
@ -832,7 +831,7 @@ class TestCache:
assert settings.preload.facial_recognition.detection == "buffalo_s"
model_cache = ModelCache()
monkeypatch.setattr("app.main.model_cache", model_cache)
monkeypatch.setattr("immich_ml.main.model_cache", model_cache)
await preload_models(settings.preload)
mock_get_model.assert_has_calls(

298
machine-learning/uv.lock generated
View File

@ -927,155 +927,7 @@ wheels = [
]
[[package]]
name = "iniconfig"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
]
[[package]]
name = "insightface"
version = "0.7.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "albumentations" },
{ name = "cython" },
{ name = "easydict" },
{ name = "matplotlib" },
{ name = "numpy" },
{ name = "onnx" },
{ name = "pillow" },
{ name = "prettytable" },
{ name = "requests" },
{ name = "scikit-image" },
{ name = "scikit-learn" },
{ name = "scipy" },
{ name = "tqdm" },
]
sdist = { url = "https://files.pythonhosted.org/packages/0b/8d/0f4af90999ca96cf8cb846eb5ae27c5ef5b390f9c090dd19e4fa76364c13/insightface-0.7.3.tar.gz", hash = "sha256:f191f719612ebb37018f41936814500544cd0f86e6fcd676c023f354c668ddf7", size = 439490 }
[[package]]
name = "itsdangerous"
version = "2.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/7f/a1/d3fb83e7a61fa0c0d3d08ad0a94ddbeff3731c05212617dff3a94e097f08/itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a", size = 56143 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", size = 15749 },
]
[[package]]
name = "jinja2"
version = "3.1.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markupsafe" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 },
]
[[package]]
name = "joblib"
version = "1.3.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/15/0f/d3b33b9f106dddef461f6df1872b7881321b247f3d255b87f61a7636f7fe/joblib-1.3.2.tar.gz", hash = "sha256:92f865e621e17784e7955080b6d042489e3b8e294949cc44c6eac304f59772b1", size = 1987720 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/10/40/d551139c85db202f1f384ba8bcf96aca2f329440a844f924c8a0040b6d02/joblib-1.3.2-py3-none-any.whl", hash = "sha256:ef4331c65f239985f3f2220ecc87db222f08fd22097a3dd5698f693875f8cbb9", size = 302207 },
]
[[package]]
name = "kiwisolver"
version = "1.4.5"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b9/2d/226779e405724344fc678fcc025b812587617ea1a48b9442628b688e85ea/kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec", size = 97552 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f1/56/cb02dcefdaab40df636b91e703b172966b444605a0ea313549f3ffc05bd3/kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af", size = 127397 },
{ url = "https://files.pythonhosted.org/packages/0e/c1/d084f8edb26533a191415d5173157080837341f9a06af9dd1a75f727abb4/kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3", size = 68125 },
{ url = "https://files.pythonhosted.org/packages/23/11/6fb190bae4b279d712a834e7b1da89f6dcff6791132f7399aa28a57c3565/kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4", size = 66211 },
{ url = "https://files.pythonhosted.org/packages/b3/13/5e9e52feb33e9e063f76b2c5eb09cb977f5bba622df3210081bfb26ec9a3/kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1", size = 1637145 },
{ url = "https://files.pythonhosted.org/packages/6f/40/4ab1fdb57fced80ce5903f04ae1aed7c1d5939dda4fd0c0aa526c12fe28a/kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff", size = 1617849 },
{ url = "https://files.pythonhosted.org/packages/49/ca/61ef43bd0832c7253b370735b0c38972c140c8774889b884372a629a8189/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a", size = 1400921 },
{ url = "https://files.pythonhosted.org/packages/68/6f/854f6a845c00b4257482468e08d8bc386f4929ee499206142378ba234419/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa", size = 1513009 },
{ url = "https://files.pythonhosted.org/packages/50/65/76f303377167d12eb7a9b423d6771b39fe5c4373e4a42f075805b1f581ae/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c", size = 1444819 },
{ url = "https://files.pythonhosted.org/packages/7e/ee/98cdf9dde129551467138b6e18cc1cc901e75ecc7ffb898c6f49609f33b1/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b", size = 1817054 },
{ url = "https://files.pythonhosted.org/packages/e6/5b/ab569016ec4abc7b496f6cb8a3ab511372c99feb6a23d948cda97e0db6da/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770", size = 1918613 },
{ url = "https://files.pythonhosted.org/packages/93/ac/39b9f99d2474b1ac7af1ddfe5756ddf9b6a8f24c5f3a32cd4c010317fc6b/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0", size = 1872650 },
{ url = "https://files.pythonhosted.org/packages/40/5b/be568548266516b114d1776120281ea9236c732fb6032a1f8f3b1e5e921c/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525", size = 1827415 },
{ url = "https://files.pythonhosted.org/packages/d4/80/c0c13d2a17a12937a19ef378bf35e94399fd171ed6ec05bcee0f038e1eaf/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b", size = 1838094 },
{ url = "https://files.pythonhosted.org/packages/70/d1/5ab93ee00ca5af708929cc12fbe665b6f1ed4ad58088e70dc00e87e0d107/kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238", size = 46585 },
{ url = "https://files.pythonhosted.org/packages/4a/a1/8a9c9be45c642fa12954855d8b3a02d9fd8551165a558835a19508fec2e6/kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276", size = 56095 },
{ url = "https://files.pythonhosted.org/packages/2a/eb/9e099ad7c47c279995d2d20474e1821100a5f10f847739bd65b1c1f02442/kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5", size = 127403 },
{ url = "https://files.pythonhosted.org/packages/a6/94/695922e71288855fc7cace3bdb52edda9d7e50edba77abb0c9d7abb51e96/kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90", size = 68156 },
{ url = "https://files.pythonhosted.org/packages/4a/fe/23d7fa78f7c66086d196406beb1fb2eaf629dd7adc01c3453033303d17fa/kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797", size = 66166 },
{ url = "https://files.pythonhosted.org/packages/f1/68/f472bf16c9141bb1bea5c0b8c66c68fc1ccb048efdbd8f0872b92125724e/kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9", size = 1334300 },
{ url = "https://files.pythonhosted.org/packages/8d/26/b4569d1f29751fca22ee915b4ebfef5974f4ef239b3335fc072882bd62d9/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437", size = 1426579 },
{ url = "https://files.pythonhosted.org/packages/f3/a3/804fc7c8bf233806ec0321c9da35971578620f2ab4fafe67d76100b3ce52/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9", size = 1541360 },
{ url = "https://files.pythonhosted.org/packages/07/ef/286e1d26524854f6fbd6540e8364d67a8857d61038ac743e11edc42fe217/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da", size = 1470091 },
{ url = "https://files.pythonhosted.org/packages/17/ba/17a706b232308e65f57deeccae503c268292e6a091313f6ce833a23093ea/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e", size = 1426259 },
{ url = "https://files.pythonhosted.org/packages/d0/f3/a0925611c9d6c2f37c5935a39203cadec6883aa914e013b46c84c4c2e641/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8", size = 1847516 },
{ url = "https://files.pythonhosted.org/packages/da/85/82d59bb8f7c4c9bb2785138b72462cb1b161668f8230c58bbb28c0403cd5/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d", size = 1946228 },
{ url = "https://files.pythonhosted.org/packages/34/3c/6a37f444c0233993881e5db3a6a1775925d4d9d2f2609bb325bb1348ed94/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0", size = 1901716 },
{ url = "https://files.pythonhosted.org/packages/cd/7e/180425790efc00adfd47db14e1e341cb4826516982334129012b971121a6/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f", size = 1852871 },
{ url = "https://files.pythonhosted.org/packages/1b/9a/13c68b2edb1fa74321e60893a9a5829788e135138e68060cf44e2d92d2c3/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f", size = 1870265 },
{ url = "https://files.pythonhosted.org/packages/9f/0a/fa56a0fdee5da2b4c79899c0f6bd1aefb29d9438c2d66430e78793571c6b/kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac", size = 46649 },
{ url = "https://files.pythonhosted.org/packages/1e/37/d3c2d4ba2719059a0f12730947bbe1ad5ee8bff89e8c35319dcb2c9ddb4c/kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355", size = 56116 },
{ url = "https://files.pythonhosted.org/packages/f3/7a/debbce859be1a2711eb8437818107137192007b88d17b5cfdb556f457b42/kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a", size = 125484 },
{ url = "https://files.pythonhosted.org/packages/2d/e0/bf8df75ba93b9e035cc6757dd5dcaf63084fdc1c846ae134e818bd7e0f03/kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192", size = 67332 },
{ url = "https://files.pythonhosted.org/packages/26/61/58bb691f6880588be3a4801d199bd776032ece07203faf3e4a8b377f7d9b/kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45", size = 64987 },
{ url = "https://files.pythonhosted.org/packages/8e/a3/96ac5413068b237c006f54dd8d70114e8756d70e3da7613c5aef20627e22/kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7", size = 1370613 },
{ url = "https://files.pythonhosted.org/packages/4d/12/f48539e6e17068b59c7f12f4d6214b973431b8e3ac83af525cafd27cebec/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db", size = 1463183 },
{ url = "https://files.pythonhosted.org/packages/f3/70/26c99be8eb034cc8e3f62e0760af1fbdc97a842a7cbc252f7978507d41c2/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff", size = 1581248 },
{ url = "https://files.pythonhosted.org/packages/17/f6/f75f20e543639b09b2de7fc864274a5a9b96cda167a6210a1d9d19306b9d/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228", size = 1508815 },
{ url = "https://files.pythonhosted.org/packages/e3/d5/bc0f22ac108743062ab703f8d6d71c9c7b077b8839fa358700bfb81770b8/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16", size = 1466042 },
{ url = "https://files.pythonhosted.org/packages/75/18/98142500f21d6838bcab49ec919414a1f0c6d049d21ddadf139124db6a70/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9", size = 1885159 },
{ url = "https://files.pythonhosted.org/packages/21/49/a241eff9e0ee013368c1d17957f9d345b0957493c3a43d82ebb558c90b0a/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162", size = 1981694 },
{ url = "https://files.pythonhosted.org/packages/90/90/9490c3de4788123041b1d600d64434f1eed809a2ce9f688075a22166b289/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4", size = 1941579 },
{ url = "https://files.pythonhosted.org/packages/b7/bb/a0cc488ef2aa92d7d304318c8549d3ec8dfe6dd3c2c67a44e3922b77bc4f/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3", size = 1888168 },
{ url = "https://files.pythonhosted.org/packages/4f/e9/9c0de8e45fef3d63f85eed3b1757f9aa511065942866331ef8b99421f433/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a", size = 1908464 },
{ url = "https://files.pythonhosted.org/packages/a3/60/4f0fd50b08f5be536ea0cef518ac7255d9dab43ca40f3b93b60e3ddf80dd/kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20", size = 46473 },
{ url = "https://files.pythonhosted.org/packages/63/50/2746566bdf4a6a842d117367d05c90cfb87ac04e9e2845aa1fa21f071362/kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9", size = 56004 },
]
[[package]]
name = "lazy-loader"
version = "0.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/0e/3a/1630a735bfdf9eb857a3b9a53317a1e1658ea97a1b4b39dcb0f71dae81f8/lazy_loader-0.3.tar.gz", hash = "sha256:3b68898e34f5b2a29daaaac172c6555512d0f32074f147e2254e4a6d9d838f37", size = 12268 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a1/c3/65b3814e155836acacf720e5be3b5757130346670ac454fee29d3eda1381/lazy_loader-0.3-py3-none-any.whl", hash = "sha256:1e9e76ee8631e264c62ce10006718e80b2cfc74340d17d1031e0f84af7478554", size = 9087 },
]
[[package]]
name = "locust"
version = "2.33.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "configargparse" },
{ name = "flask" },
{ name = "flask-cors" },
{ name = "flask-login" },
{ name = "gevent", marker = "python_full_version != '3.13.*'" },
{ name = "geventhttpclient" },
{ name = "msgpack" },
{ name = "psutil" },
{ name = "pywin32", marker = "sys_platform == 'win32'" },
{ name = "pyzmq" },
{ name = "requests" },
{ name = "setuptools" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
{ name = "werkzeug" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a2/9e/09ee87dc12b240248731080bfd460c7d384aadb3171f6d03a4e7314cd0e1/locust-2.33.2.tar.gz", hash = "sha256:e626ed0156f36cec94c3c6b030fc91046469e7e2f5c2e91a99aab0f28b84977e", size = 2237716 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9c/c7/bb55ac53173d3e92b1b2577d0f36439500406ca5be476a27b7bc01ae8a75/locust-2.33.2-py3-none-any.whl", hash = "sha256:a2f3b53dcd5ed22cecee874cd989912749663d82ec9b030637d3e43044e5878e", size = 2254591 },
]
[[package]]
name = "machine-learning"
name = "immich-ml"
version = "1.129.0"
source = { editable = "." }
dependencies = [
@ -1224,6 +1076,154 @@ types = [
{ name = "types-ujson", specifier = ">=5.10.0.20240515" },
]
[[package]]
name = "iniconfig"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
]
[[package]]
name = "insightface"
version = "0.7.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "albumentations" },
{ name = "cython" },
{ name = "easydict" },
{ name = "matplotlib" },
{ name = "numpy" },
{ name = "onnx" },
{ name = "pillow" },
{ name = "prettytable" },
{ name = "requests" },
{ name = "scikit-image" },
{ name = "scikit-learn" },
{ name = "scipy" },
{ name = "tqdm" },
]
sdist = { url = "https://files.pythonhosted.org/packages/0b/8d/0f4af90999ca96cf8cb846eb5ae27c5ef5b390f9c090dd19e4fa76364c13/insightface-0.7.3.tar.gz", hash = "sha256:f191f719612ebb37018f41936814500544cd0f86e6fcd676c023f354c668ddf7", size = 439490 }
[[package]]
name = "itsdangerous"
version = "2.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/7f/a1/d3fb83e7a61fa0c0d3d08ad0a94ddbeff3731c05212617dff3a94e097f08/itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a", size = 56143 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", size = 15749 },
]
[[package]]
name = "jinja2"
version = "3.1.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markupsafe" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 },
]
[[package]]
name = "joblib"
version = "1.3.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/15/0f/d3b33b9f106dddef461f6df1872b7881321b247f3d255b87f61a7636f7fe/joblib-1.3.2.tar.gz", hash = "sha256:92f865e621e17784e7955080b6d042489e3b8e294949cc44c6eac304f59772b1", size = 1987720 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/10/40/d551139c85db202f1f384ba8bcf96aca2f329440a844f924c8a0040b6d02/joblib-1.3.2-py3-none-any.whl", hash = "sha256:ef4331c65f239985f3f2220ecc87db222f08fd22097a3dd5698f693875f8cbb9", size = 302207 },
]
[[package]]
name = "kiwisolver"
version = "1.4.5"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b9/2d/226779e405724344fc678fcc025b812587617ea1a48b9442628b688e85ea/kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec", size = 97552 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f1/56/cb02dcefdaab40df636b91e703b172966b444605a0ea313549f3ffc05bd3/kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af", size = 127397 },
{ url = "https://files.pythonhosted.org/packages/0e/c1/d084f8edb26533a191415d5173157080837341f9a06af9dd1a75f727abb4/kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3", size = 68125 },
{ url = "https://files.pythonhosted.org/packages/23/11/6fb190bae4b279d712a834e7b1da89f6dcff6791132f7399aa28a57c3565/kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4", size = 66211 },
{ url = "https://files.pythonhosted.org/packages/b3/13/5e9e52feb33e9e063f76b2c5eb09cb977f5bba622df3210081bfb26ec9a3/kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1", size = 1637145 },
{ url = "https://files.pythonhosted.org/packages/6f/40/4ab1fdb57fced80ce5903f04ae1aed7c1d5939dda4fd0c0aa526c12fe28a/kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff", size = 1617849 },
{ url = "https://files.pythonhosted.org/packages/49/ca/61ef43bd0832c7253b370735b0c38972c140c8774889b884372a629a8189/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a", size = 1400921 },
{ url = "https://files.pythonhosted.org/packages/68/6f/854f6a845c00b4257482468e08d8bc386f4929ee499206142378ba234419/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa", size = 1513009 },
{ url = "https://files.pythonhosted.org/packages/50/65/76f303377167d12eb7a9b423d6771b39fe5c4373e4a42f075805b1f581ae/kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c", size = 1444819 },
{ url = "https://files.pythonhosted.org/packages/7e/ee/98cdf9dde129551467138b6e18cc1cc901e75ecc7ffb898c6f49609f33b1/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b", size = 1817054 },
{ url = "https://files.pythonhosted.org/packages/e6/5b/ab569016ec4abc7b496f6cb8a3ab511372c99feb6a23d948cda97e0db6da/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770", size = 1918613 },
{ url = "https://files.pythonhosted.org/packages/93/ac/39b9f99d2474b1ac7af1ddfe5756ddf9b6a8f24c5f3a32cd4c010317fc6b/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0", size = 1872650 },
{ url = "https://files.pythonhosted.org/packages/40/5b/be568548266516b114d1776120281ea9236c732fb6032a1f8f3b1e5e921c/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525", size = 1827415 },
{ url = "https://files.pythonhosted.org/packages/d4/80/c0c13d2a17a12937a19ef378bf35e94399fd171ed6ec05bcee0f038e1eaf/kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b", size = 1838094 },
{ url = "https://files.pythonhosted.org/packages/70/d1/5ab93ee00ca5af708929cc12fbe665b6f1ed4ad58088e70dc00e87e0d107/kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238", size = 46585 },
{ url = "https://files.pythonhosted.org/packages/4a/a1/8a9c9be45c642fa12954855d8b3a02d9fd8551165a558835a19508fec2e6/kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276", size = 56095 },
{ url = "https://files.pythonhosted.org/packages/2a/eb/9e099ad7c47c279995d2d20474e1821100a5f10f847739bd65b1c1f02442/kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5", size = 127403 },
{ url = "https://files.pythonhosted.org/packages/a6/94/695922e71288855fc7cace3bdb52edda9d7e50edba77abb0c9d7abb51e96/kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90", size = 68156 },
{ url = "https://files.pythonhosted.org/packages/4a/fe/23d7fa78f7c66086d196406beb1fb2eaf629dd7adc01c3453033303d17fa/kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797", size = 66166 },
{ url = "https://files.pythonhosted.org/packages/f1/68/f472bf16c9141bb1bea5c0b8c66c68fc1ccb048efdbd8f0872b92125724e/kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9", size = 1334300 },
{ url = "https://files.pythonhosted.org/packages/8d/26/b4569d1f29751fca22ee915b4ebfef5974f4ef239b3335fc072882bd62d9/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437", size = 1426579 },
{ url = "https://files.pythonhosted.org/packages/f3/a3/804fc7c8bf233806ec0321c9da35971578620f2ab4fafe67d76100b3ce52/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9", size = 1541360 },
{ url = "https://files.pythonhosted.org/packages/07/ef/286e1d26524854f6fbd6540e8364d67a8857d61038ac743e11edc42fe217/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da", size = 1470091 },
{ url = "https://files.pythonhosted.org/packages/17/ba/17a706b232308e65f57deeccae503c268292e6a091313f6ce833a23093ea/kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e", size = 1426259 },
{ url = "https://files.pythonhosted.org/packages/d0/f3/a0925611c9d6c2f37c5935a39203cadec6883aa914e013b46c84c4c2e641/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8", size = 1847516 },
{ url = "https://files.pythonhosted.org/packages/da/85/82d59bb8f7c4c9bb2785138b72462cb1b161668f8230c58bbb28c0403cd5/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d", size = 1946228 },
{ url = "https://files.pythonhosted.org/packages/34/3c/6a37f444c0233993881e5db3a6a1775925d4d9d2f2609bb325bb1348ed94/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0", size = 1901716 },
{ url = "https://files.pythonhosted.org/packages/cd/7e/180425790efc00adfd47db14e1e341cb4826516982334129012b971121a6/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f", size = 1852871 },
{ url = "https://files.pythonhosted.org/packages/1b/9a/13c68b2edb1fa74321e60893a9a5829788e135138e68060cf44e2d92d2c3/kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f", size = 1870265 },
{ url = "https://files.pythonhosted.org/packages/9f/0a/fa56a0fdee5da2b4c79899c0f6bd1aefb29d9438c2d66430e78793571c6b/kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac", size = 46649 },
{ url = "https://files.pythonhosted.org/packages/1e/37/d3c2d4ba2719059a0f12730947bbe1ad5ee8bff89e8c35319dcb2c9ddb4c/kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355", size = 56116 },
{ url = "https://files.pythonhosted.org/packages/f3/7a/debbce859be1a2711eb8437818107137192007b88d17b5cfdb556f457b42/kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a", size = 125484 },
{ url = "https://files.pythonhosted.org/packages/2d/e0/bf8df75ba93b9e035cc6757dd5dcaf63084fdc1c846ae134e818bd7e0f03/kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192", size = 67332 },
{ url = "https://files.pythonhosted.org/packages/26/61/58bb691f6880588be3a4801d199bd776032ece07203faf3e4a8b377f7d9b/kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45", size = 64987 },
{ url = "https://files.pythonhosted.org/packages/8e/a3/96ac5413068b237c006f54dd8d70114e8756d70e3da7613c5aef20627e22/kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7", size = 1370613 },
{ url = "https://files.pythonhosted.org/packages/4d/12/f48539e6e17068b59c7f12f4d6214b973431b8e3ac83af525cafd27cebec/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db", size = 1463183 },
{ url = "https://files.pythonhosted.org/packages/f3/70/26c99be8eb034cc8e3f62e0760af1fbdc97a842a7cbc252f7978507d41c2/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff", size = 1581248 },
{ url = "https://files.pythonhosted.org/packages/17/f6/f75f20e543639b09b2de7fc864274a5a9b96cda167a6210a1d9d19306b9d/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228", size = 1508815 },
{ url = "https://files.pythonhosted.org/packages/e3/d5/bc0f22ac108743062ab703f8d6d71c9c7b077b8839fa358700bfb81770b8/kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16", size = 1466042 },
{ url = "https://files.pythonhosted.org/packages/75/18/98142500f21d6838bcab49ec919414a1f0c6d049d21ddadf139124db6a70/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9", size = 1885159 },
{ url = "https://files.pythonhosted.org/packages/21/49/a241eff9e0ee013368c1d17957f9d345b0957493c3a43d82ebb558c90b0a/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162", size = 1981694 },
{ url = "https://files.pythonhosted.org/packages/90/90/9490c3de4788123041b1d600d64434f1eed809a2ce9f688075a22166b289/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4", size = 1941579 },
{ url = "https://files.pythonhosted.org/packages/b7/bb/a0cc488ef2aa92d7d304318c8549d3ec8dfe6dd3c2c67a44e3922b77bc4f/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3", size = 1888168 },
{ url = "https://files.pythonhosted.org/packages/4f/e9/9c0de8e45fef3d63f85eed3b1757f9aa511065942866331ef8b99421f433/kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a", size = 1908464 },
{ url = "https://files.pythonhosted.org/packages/a3/60/4f0fd50b08f5be536ea0cef518ac7255d9dab43ca40f3b93b60e3ddf80dd/kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20", size = 46473 },
{ url = "https://files.pythonhosted.org/packages/63/50/2746566bdf4a6a842d117367d05c90cfb87ac04e9e2845aa1fa21f071362/kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9", size = 56004 },
]
[[package]]
name = "lazy-loader"
version = "0.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/0e/3a/1630a735bfdf9eb857a3b9a53317a1e1658ea97a1b4b39dcb0f71dae81f8/lazy_loader-0.3.tar.gz", hash = "sha256:3b68898e34f5b2a29daaaac172c6555512d0f32074f147e2254e4a6d9d838f37", size = 12268 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a1/c3/65b3814e155836acacf720e5be3b5757130346670ac454fee29d3eda1381/lazy_loader-0.3-py3-none-any.whl", hash = "sha256:1e9e76ee8631e264c62ce10006718e80b2cfc74340d17d1031e0f84af7478554", size = 9087 },
]
[[package]]
name = "locust"
version = "2.33.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "configargparse" },
{ name = "flask" },
{ name = "flask-cors" },
{ name = "flask-login" },
{ name = "gevent", marker = "python_full_version != '3.13.*'" },
{ name = "geventhttpclient" },
{ name = "msgpack" },
{ name = "psutil" },
{ name = "pywin32", marker = "sys_platform == 'win32'" },
{ name = "pyzmq" },
{ name = "requests" },
{ name = "setuptools" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
{ name = "werkzeug" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a2/9e/09ee87dc12b240248731080bfd460c7d384aadb3171f6d03a4e7314cd0e1/locust-2.33.2.tar.gz", hash = "sha256:e626ed0156f36cec94c3c6b030fc91046469e7e2f5c2e91a99aab0f28b84977e", size = 2237716 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9c/c7/bb55ac53173d3e92b1b2577d0f36439500406ca5be476a27b7bc01ae8a75/locust-2.33.2-py3-none-any.whl", hash = "sha256:a2f3b53dcd5ed22cecee874cd989912749663d82ec9b030637d3e43044e5878e", size = 2254591 },
]
[[package]]
name = "markdown-it-py"
version = "3.0.0"

View File

@ -35,8 +35,8 @@ platform :android do
task: 'bundle',
build_type: 'Release',
properties: {
"android.injected.version.code" => 190,
"android.injected.version.name" => "1.130.2",
"android.injected.version.code" => 191,
"android.injected.version.name" => "1.130.3",
}
)
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')

View File

@ -541,7 +541,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 198;
CURRENT_PROJECT_VERSION = 199;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_BITCODE = NO;
@ -685,7 +685,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 198;
CURRENT_PROJECT_VERSION = 199;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_BITCODE = NO;
@ -715,7 +715,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 198;
CURRENT_PROJECT_VERSION = 199;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_BITCODE = NO;
@ -748,7 +748,7 @@
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 198;
CURRENT_PROJECT_VERSION = 199;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
@ -791,7 +791,7 @@
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 198;
CURRENT_PROJECT_VERSION = 199;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
@ -831,7 +831,7 @@
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 198;
CURRENT_PROJECT_VERSION = 199;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_USER_SCRIPT_SANDBOXING = YES;

View File

@ -78,7 +78,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.130.0</string>
<string>1.130.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
@ -93,7 +93,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>198</string>
<string>199</string>
<key>FLTEnableImpeller</key>
<true/>
<key>ITSAppUsesNonExemptEncryption</key>

View File

@ -19,7 +19,7 @@ platform :ios do
desc "iOS Release"
lane :release do
increment_version_number(
version_number: "1.130.2"
version_number: "1.130.3"
)
increment_build_number(
build_number: latest_testflight_build_number + 1,

View File

@ -737,6 +737,11 @@ class SyncService {
album.thumbnail.value = thumb;
try {
await _albumRepository.create(album);
final int assetCount =
await _albumMediaRepository.getAssetCount(album.localId!);
await _eTagRepository.upsertAll([
ETag(id: album.eTagKeyAssetCount, assetCount: assetCount),
]);
_log.info("Added a new local album to DB: ${album.name}");
} catch (e) {
_log.severe("Failed to add new local album ${album.name} to DB", e);

View File

@ -210,6 +210,9 @@ class LoginForm extends HookConsumerWidget {
.getOAuthServerUrl(sanitizeUrl(serverEndpointController.text));
isLoading.value = true;
// Invalidate all api repository provider instance to take into account new access token
invalidateAllApiRepositoryProviders(ref);
} catch (error, stack) {
log.severe('Error getting OAuth server Url: $error', stack);

View File

@ -3,7 +3,7 @@ Immich API
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
- API version: 1.130.2
- API version: 1.130.3
- Generator version: 7.8.0
- Build package: org.openapitools.codegen.languages.DartClientCodegen

View File

@ -2,7 +2,7 @@ name: immich_mobile
description: Immich - selfhosted backup media file on mobile phone
publish_to: 'none'
version: 1.130.2+190
version: 1.130.3+191
environment:
sdk: '>=3.3.0 <4.0.0'

View File

@ -7656,7 +7656,7 @@
"info": {
"title": "Immich",
"description": "Immich API",
"version": "1.130.2",
"version": "1.130.3",
"contact": {}
},
"tags": [],

View File

@ -1,12 +1,12 @@
{
"name": "@immich/sdk",
"version": "1.130.2",
"version": "1.130.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@immich/sdk",
"version": "1.130.2",
"version": "1.130.3",
"license": "GNU Affero General Public License version 3",
"dependencies": {
"@oazapfts/runtime": "^1.0.2"

View File

@ -1,6 +1,6 @@
{
"name": "@immich/sdk",
"version": "1.130.2",
"version": "1.130.3",
"description": "Auto-generated TypeScript SDK for the Immich API",
"type": "module",
"main": "./build/index.js",

View File

@ -1,6 +1,6 @@
/**
* Immich
* 1.130.2
* 1.130.3
* DO NOT MODIFY - This file has been generated using oazapfts.
* See https://www.npmjs.com/package/oazapfts
*/

1571
server/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "immich",
"version": "1.130.2",
"version": "1.130.3",
"description": "",
"author": "",
"private": true,
@ -44,11 +44,11 @@
"@nestjs/schedule": "^5.0.0",
"@nestjs/swagger": "^11.0.2",
"@nestjs/websockets": "^11.0.4",
"@opentelemetry/auto-instrumentations-node": "^0.56.0",
"@opentelemetry/auto-instrumentations-node": "^0.57.0",
"@opentelemetry/context-async-hooks": "^1.24.0",
"@opentelemetry/exporter-prometheus": "^0.57.0",
"@opentelemetry/sdk-node": "^0.57.0",
"@react-email/components": "^0.0.33",
"@opentelemetry/exporter-prometheus": "^0.200.0",
"@opentelemetry/sdk-node": "^0.200.0",
"@react-email/components": "^0.0.34",
"@socket.io/redis-adapter": "^8.3.0",
"archiver": "^7.0.0",
"async-lock": "^1.4.0",

View File

@ -93,7 +93,7 @@ select
"exif" as "exifInfo"
from
"assets"
inner join "exif" on "assets"."id" = "exif"."assetId"
left join "exif" on "assets"."id" = "exif"."assetId"
inner join "albums_assets_assets" on "albums_assets_assets"."assetsId" = "assets"."id"
where
"albums_assets_assets"."albumsId" = "albums"."id"

View File

@ -68,7 +68,7 @@ const withAssets = (eb: ExpressionBuilder<DB, 'albums'>) => {
eb
.selectFrom('assets')
.selectAll('assets')
.innerJoin('exif', 'assets.id', 'exif.assetId')
.leftJoin('exif', 'assets.id', 'exif.assetId')
.select((eb) => eb.table('exif').as('exifInfo'))
.innerJoin('albums_assets_assets', 'albums_assets_assets.assetsId', 'assets.id')
.whereRef('albums_assets_assets.albumsId', '=', 'albums.id')

View File

@ -7,8 +7,10 @@ import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
import { IORedisInstrumentation } from '@opentelemetry/instrumentation-ioredis';
import { NestInstrumentation } from '@opentelemetry/instrumentation-nestjs-core';
import { PgInstrumentation } from '@opentelemetry/instrumentation-pg';
import { NodeSDK, contextBase, metrics, resources } from '@opentelemetry/sdk-node';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
import { resourceFromAttributes } from '@opentelemetry/resources';
import { AggregationType } from '@opentelemetry/sdk-metrics';
import { NodeSDK, contextBase } from '@opentelemetry/sdk-node';
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
import { ClassConstructor } from 'class-transformer';
import { snakeCase, startCase } from 'lodash';
import { MetricService } from 'nestjs-otel';
@ -49,10 +51,9 @@ export class MetricGroupRepository {
}
}
const aggregation = new metrics.ExplicitBucketHistogramAggregation(
[0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10_000],
true,
);
const aggregationBoundaries = [
0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10_000,
];
let instance: NodeSDK | undefined;
@ -61,9 +62,9 @@ export const bootstrapTelemetry = (port: number) => {
throw new Error('OpenTelemetry SDK already started');
}
instance = new NodeSDK({
resource: new resources.Resource({
[SemanticResourceAttributes.SERVICE_NAME]: `immich`,
[SemanticResourceAttributes.SERVICE_VERSION]: serverVersion.toString(),
resource: resourceFromAttributes({
[ATTR_SERVICE_NAME]: `immich`,
[ATTR_SERVICE_VERSION]: serverVersion.toString(),
}),
metricReader: new PrometheusExporter({ port }),
contextManager: new AsyncLocalStorageContextManager(),
@ -73,7 +74,16 @@ export const bootstrapTelemetry = (port: number) => {
new NestInstrumentation(),
new PgInstrumentation(),
],
views: [new metrics.View({ aggregation, instrumentName: '*', instrumentUnit: 'ms' })],
views: [
{
instrumentName: '*',
instrumentUnit: 'ms',
aggregation: {
type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM,
options: { boundaries: aggregationBoundaries },
},
},
],
});
instance.start();

236
web/package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "immich-web",
"version": "1.130.2",
"version": "1.130.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "immich-web",
"version": "1.130.2",
"version": "1.130.3",
"license": "GNU Affero General Public License version 3",
"dependencies": {
"@formatjs/icu-messageformat-parser": "^2.9.8",
@ -70,7 +70,7 @@
"prettier-plugin-sort-json": "^4.1.1",
"prettier-plugin-svelte": "^3.3.3",
"rollup-plugin-visualizer": "^5.14.0",
"svelte": "^5.22.6",
"svelte": "^5.25.3",
"svelte-check": "^4.1.5",
"tailwindcss": "^3.4.17",
"tslib": "^2.6.2",
@ -81,7 +81,7 @@
},
"../open-api/typescript-sdk": {
"name": "@immich/sdk",
"version": "1.130.2",
"version": "1.130.3",
"license": "GNU Affero General Public License version 3",
"dependencies": {
"@oazapfts/runtime": "^1.0.2"
@ -710,9 +710,9 @@
}
},
"node_modules/@eslint/config-helpers": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.1.0.tgz",
"integrity": "sha512-kLrdPDJE1ckPo94kmPPf9Hfd0DU0Jw6oKYrhe+pwSC0iTUInmTa+w6fw8sGgcfkFJGNdWOUeOaDM4quW4a7OkA==",
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.0.tgz",
"integrity": "sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@ -733,9 +733,9 @@
}
},
"node_modules/@eslint/eslintrc": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz",
"integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==",
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
"integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -770,9 +770,9 @@
}
},
"node_modules/@eslint/js": {
"version": "9.22.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.22.0.tgz",
"integrity": "sha512-vLFajx9o8d1/oL2ZkpMYbkLv8nDB6yaIwFNt7nI4+I80U/z03SxmfOMsLbvWr3p7C+Wnoh//aOu2pQW8cS0HCQ==",
"version": "9.23.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz",
"integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==",
"dev": true,
"license": "MIT",
"engines": {
@ -2112,9 +2112,9 @@
}
},
"node_modules/@sveltejs/kit": {
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.19.0.tgz",
"integrity": "sha512-UTx28Ad4sYsLU//gqkEo5aFOPFBRT2uXCmXTsURqhurDCvzkVwXruJgBcHDaMiK6RKKpYRteDUaXYqZyGPgCXQ==",
"version": "2.20.2",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.20.2.tgz",
"integrity": "sha512-Dv8TOAZC9vyfcAB9TMsvUEJsRbklRTeNfcYBPaeH6KnABJ99i3CvCB2eNx8fiiliIqe+9GIchBg4RodRH5p1BQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -2582,17 +2582,17 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.26.1.tgz",
"integrity": "sha512-2X3mwqsj9Bd3Ciz508ZUtoQQYpOhU/kWoUqIf49H8Z0+Vbh6UF/y0OEYp0Q0axOGzaBGs7QxRwq0knSQ8khQNA==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.27.0.tgz",
"integrity": "sha512-4henw4zkePi5p252c8ncBLzLce52SEUz2Ebj8faDnuUXz2UuHEONYcJ+G0oaCF+bYCWVZtrGzq3FD7YXetmnSA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.26.1",
"@typescript-eslint/type-utils": "8.26.1",
"@typescript-eslint/utils": "8.26.1",
"@typescript-eslint/visitor-keys": "8.26.1",
"@typescript-eslint/scope-manager": "8.27.0",
"@typescript-eslint/type-utils": "8.27.0",
"@typescript-eslint/utils": "8.27.0",
"@typescript-eslint/visitor-keys": "8.27.0",
"graphemer": "^1.4.0",
"ignore": "^5.3.1",
"natural-compare": "^1.4.0",
@ -2612,16 +2612,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.26.1.tgz",
"integrity": "sha512-w6HZUV4NWxqd8BdeFf81t07d7/YV9s7TCWrQQbG5uhuvGUAW+fq1usZ1Hmz9UPNLniFnD8GLSsDpjP0hm1S4lQ==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.27.0.tgz",
"integrity": "sha512-XGwIabPallYipmcOk45DpsBSgLC64A0yvdAkrwEzwZ2viqGqRUJ8eEYoPz0CWnutgAFbNMPdsGGvzjSmcWVlEA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.26.1",
"@typescript-eslint/types": "8.26.1",
"@typescript-eslint/typescript-estree": "8.26.1",
"@typescript-eslint/visitor-keys": "8.26.1",
"@typescript-eslint/scope-manager": "8.27.0",
"@typescript-eslint/types": "8.27.0",
"@typescript-eslint/typescript-estree": "8.27.0",
"@typescript-eslint/visitor-keys": "8.27.0",
"debug": "^4.3.4"
},
"engines": {
@ -2637,14 +2637,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.26.1.tgz",
"integrity": "sha512-6EIvbE5cNER8sqBu6V7+KeMZIC1664d2Yjt+B9EWUXrsyWpxx4lEZrmvxgSKRC6gX+efDL/UY9OpPZ267io3mg==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.27.0.tgz",
"integrity": "sha512-8oI9GwPMQmBryaaxG1tOZdxXVeMDte6NyJA4i7/TWa4fBwgnAXYlIQP+uYOeqAaLJ2JRxlG9CAyL+C+YE9Xknw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.26.1",
"@typescript-eslint/visitor-keys": "8.26.1"
"@typescript-eslint/types": "8.27.0",
"@typescript-eslint/visitor-keys": "8.27.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -2655,14 +2655,14 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.26.1.tgz",
"integrity": "sha512-Kcj/TagJLwoY/5w9JGEFV0dclQdyqw9+VMndxOJKtoFSjfZhLXhYjzsQEeyza03rwHx2vFEGvrJWJBXKleRvZg==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.27.0.tgz",
"integrity": "sha512-wVArTVcz1oJOIEJxui/nRhV0TXzD/zMSOYi/ggCfNq78EIszddXcJb7r4RCp/oBrjt8n9A0BSxRMKxHftpDxDA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/typescript-estree": "8.26.1",
"@typescript-eslint/utils": "8.26.1",
"@typescript-eslint/typescript-estree": "8.27.0",
"@typescript-eslint/utils": "8.27.0",
"debug": "^4.3.4",
"ts-api-utils": "^2.0.1"
},
@ -2679,9 +2679,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.26.1.tgz",
"integrity": "sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.27.0.tgz",
"integrity": "sha512-/6cp9yL72yUHAYq9g6DsAU+vVfvQmd1a8KyA81uvfDE21O2DwQ/qxlM4AR8TSdAu+kJLBDrEHKC5/W2/nxsY0A==",
"dev": true,
"license": "MIT",
"engines": {
@ -2693,14 +2693,14 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.26.1.tgz",
"integrity": "sha512-yUwPpUHDgdrv1QJ7YQal3cMVBGWfnuCdKbXw1yyjArax3353rEJP1ZA+4F8nOlQ3RfS2hUN/wze3nlY+ZOhvoA==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.27.0.tgz",
"integrity": "sha512-BnKq8cqPVoMw71O38a1tEb6iebEgGA80icSxW7g+kndx0o6ot6696HjG7NdgfuAVmVEtwXUr3L8R9ZuVjoQL6A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.26.1",
"@typescript-eslint/visitor-keys": "8.26.1",
"@typescript-eslint/types": "8.27.0",
"@typescript-eslint/visitor-keys": "8.27.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
@ -2746,16 +2746,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.1.tgz",
"integrity": "sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.27.0.tgz",
"integrity": "sha512-njkodcwH1yvmo31YWgRHNb/x1Xhhq4/m81PhtvmRngD8iHPehxffz1SNCO+kwaePhATC+kOa/ggmvPoPza5i0Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@typescript-eslint/scope-manager": "8.26.1",
"@typescript-eslint/types": "8.26.1",
"@typescript-eslint/typescript-estree": "8.26.1"
"@typescript-eslint/scope-manager": "8.27.0",
"@typescript-eslint/types": "8.27.0",
"@typescript-eslint/typescript-estree": "8.27.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -2770,13 +2770,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.26.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.1.tgz",
"integrity": "sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg==",
"version": "8.27.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.27.0.tgz",
"integrity": "sha512-WsXQwMkILJvffP6z4U3FYJPlbf/j07HIxmDjZpbNvBJkMfvwXj5ACRkkHwBDvLBbDbtX5TdU64/rcvKJ/vuInQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.26.1",
"@typescript-eslint/types": "8.27.0",
"eslint-visitor-keys": "^4.2.0"
},
"engines": {
@ -2801,9 +2801,9 @@
}
},
"node_modules/@vitest/coverage-v8": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.8.tgz",
"integrity": "sha512-y7SAKsQirsEJ2F8bulBck4DoluhI2EEgTimHd6EEUgJBGKy9tC25cpywh1MH4FvDGoG2Unt7+asVd1kj4qOSAw==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.9.tgz",
"integrity": "sha512-15OACZcBtQ34keIEn19JYTVuMFTlFrClclwWjHo/IRPg/8ELpkgNTl0o7WLP9WO9XGH6+tip9CPYtEOrIDJvBA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -2824,8 +2824,8 @@
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"@vitest/browser": "3.0.8",
"vitest": "3.0.8"
"@vitest/browser": "3.0.9",
"vitest": "3.0.9"
},
"peerDependenciesMeta": {
"@vitest/browser": {
@ -2852,14 +2852,14 @@
}
},
"node_modules/@vitest/expect": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.8.tgz",
"integrity": "sha512-Xu6TTIavTvSSS6LZaA3EebWFr6tsoXPetOWNMOlc7LO88QVVBwq2oQWBoDiLCN6YTvNYsGSjqOO8CAdjom5DCQ==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.9.tgz",
"integrity": "sha512-5eCqRItYgIML7NNVgJj6TVCmdzE7ZVgJhruW0ziSQV4V7PvLkDL1bBkBdcTs/VuIz0IxPb5da1IDSqc1TR9eig==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/spy": "3.0.8",
"@vitest/utils": "3.0.8",
"@vitest/spy": "3.0.9",
"@vitest/utils": "3.0.9",
"chai": "^5.2.0",
"tinyrainbow": "^2.0.0"
},
@ -2868,13 +2868,13 @@
}
},
"node_modules/@vitest/mocker": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.8.tgz",
"integrity": "sha512-n3LjS7fcW1BCoF+zWZxG7/5XvuYH+lsFg+BDwwAz0arIwHQJFUEsKBQ0BLU49fCxuM/2HSeBPHQD8WjgrxMfow==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.9.tgz",
"integrity": "sha512-ryERPIBOnvevAkTq+L1lD+DTFBRcjueL9lOUfXsLfwP92h4e+Heb+PjiqS3/OURWPtywfafK0kj++yDFjWUmrA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/spy": "3.0.8",
"@vitest/spy": "3.0.9",
"estree-walker": "^3.0.3",
"magic-string": "^0.30.17"
},
@ -2895,9 +2895,9 @@
}
},
"node_modules/@vitest/pretty-format": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.8.tgz",
"integrity": "sha512-BNqwbEyitFhzYMYHUVbIvepOyeQOSFA/NeJMIP9enMntkkxLgOcgABH6fjyXG85ipTgvero6noreavGIqfJcIg==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.9.tgz",
"integrity": "sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -2908,13 +2908,13 @@
}
},
"node_modules/@vitest/runner": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.8.tgz",
"integrity": "sha512-c7UUw6gEcOzI8fih+uaAXS5DwjlBaCJUo7KJ4VvJcjL95+DSR1kova2hFuRt3w41KZEFcOEiq098KkyrjXeM5w==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.9.tgz",
"integrity": "sha512-NX9oUXgF9HPfJSwl8tUZCMP1oGx2+Sf+ru6d05QjzQz4OwWg0psEzwY6VexP2tTHWdOkhKHUIZH+fS6nA7jfOw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/utils": "3.0.8",
"@vitest/utils": "3.0.9",
"pathe": "^2.0.3"
},
"funding": {
@ -2922,13 +2922,13 @@
}
},
"node_modules/@vitest/snapshot": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.8.tgz",
"integrity": "sha512-x8IlMGSEMugakInj44nUrLSILh/zy1f2/BgH0UeHpNyOocG18M9CWVIFBaXPt8TrqVZWmcPjwfG/ht5tnpba8A==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.9.tgz",
"integrity": "sha512-AiLUiuZ0FuA+/8i19mTYd+re5jqjEc2jZbgJ2up0VY0Ddyyxg/uUtBDpIFAy4uzKaQxOW8gMgBdAJJ2ydhu39A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "3.0.8",
"@vitest/pretty-format": "3.0.9",
"magic-string": "^0.30.17",
"pathe": "^2.0.3"
},
@ -2937,9 +2937,9 @@
}
},
"node_modules/@vitest/spy": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.8.tgz",
"integrity": "sha512-MR+PzJa+22vFKYb934CejhR4BeRpMSoxkvNoDit68GQxRLSf11aT6CTj3XaqUU9rxgWJFnqicN/wxw6yBRkI1Q==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.9.tgz",
"integrity": "sha512-/CcK2UDl0aQ2wtkp3YVWldrpLRNCfVcIOFGlVGKO4R5eajsH393Z1yiXLVQ7vWsj26JOEjeZI0x5sm5P4OGUNQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -2950,13 +2950,13 @@
}
},
"node_modules/@vitest/utils": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.8.tgz",
"integrity": "sha512-nkBC3aEhfX2PdtQI/QwAWp8qZWwzASsU4Npbcd5RdMPBSSLCpkZp52P3xku3s3uA0HIEhGvEcF8rNkBsz9dQ4Q==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.9.tgz",
"integrity": "sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "3.0.8",
"@vitest/pretty-format": "3.0.9",
"loupe": "^3.1.3",
"tinyrainbow": "^2.0.0"
},
@ -4203,19 +4203,19 @@
}
},
"node_modules/eslint": {
"version": "9.22.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.22.0.tgz",
"integrity": "sha512-9V/QURhsRN40xuHXWjV64yvrzMjcz7ZyNoF2jJFmy9j/SLk0u1OLSZgXi28MrXjymnjEGSR80WCdab3RGMDveQ==",
"version": "9.23.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz",
"integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.19.2",
"@eslint/config-helpers": "^0.1.0",
"@eslint/config-helpers": "^0.2.0",
"@eslint/core": "^0.12.0",
"@eslint/eslintrc": "^3.3.0",
"@eslint/js": "9.22.0",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.23.0",
"@eslint/plugin-kit": "^0.2.7",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
@ -4293,9 +4293,9 @@
}
},
"node_modules/eslint-plugin-svelte": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.1.0.tgz",
"integrity": "sha512-hSQyLDkuuHPJby1ixZfUVrfLON42mT0Odf18MbwAgFUPuyIwJlhy3acUY1/bxt+Njucq/dQxR543zYDqkBNLmw==",
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.3.3.tgz",
"integrity": "sha512-imzGqIgWbfsb/CR14d3k3M8MiVNGet+l9mjPhvo1Rm0Nxi0rNn4/eELqyR8FWlgKBMlGkOp2kshRJm0xpxNfHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -8350,9 +8350,9 @@
}
},
"node_modules/svelte": {
"version": "5.23.0",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.23.0.tgz",
"integrity": "sha512-v0lL3NuKontiCxholEiAXCB+BYbndlKbwlDMK0DS86WgGELMJSpyqCSbJeMEMBDwOglnS7Ar2Rq0wwa/z2L8Vg==",
"version": "5.25.3",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.25.3.tgz",
"integrity": "sha512-J9rcZ/xVJonAoESqVGHHZhrNdVbrCfkdB41BP6eiwHMoFShD9it3yZXApVYMHdGfCshBsZCKsajwJeBbS/M1zg==",
"license": "MIT",
"dependencies": {
"@ampproject/remapping": "^2.3.0",
@ -9675,9 +9675,9 @@
}
},
"node_modules/vite-node": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.8.tgz",
"integrity": "sha512-6PhR4H9VGlcwXZ+KWCdMqbtG649xCPZqfI9j2PsK1FcXgEzro5bGHcVKFCTqPLaNKZES8Evqv4LwvZARsq5qlg==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.9.tgz",
"integrity": "sha512-w3Gdx7jDcuT9cNn9jExXgOyKmf5UOTb6WMHz8LGAm54eS1Elf5OuBhCxl6zJxGhEeIkgsE1WbHuoL0mj/UXqXg==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -9735,19 +9735,19 @@
}
},
"node_modules/vitest": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.8.tgz",
"integrity": "sha512-dfqAsNqRGUc8hB9OVR2P0w8PZPEckti2+5rdZip0WIz9WW0MnImJ8XiR61QhqLa92EQzKP2uPkzenKOAHyEIbA==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.9.tgz",
"integrity": "sha512-BbcFDqNyBlfSpATmTtXOAOj71RNKDDvjBM/uPfnxxVGrG+FSH2RQIwgeEngTaTkuU/h0ScFvf+tRcKfYXzBybQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/expect": "3.0.8",
"@vitest/mocker": "3.0.8",
"@vitest/pretty-format": "^3.0.8",
"@vitest/runner": "3.0.8",
"@vitest/snapshot": "3.0.8",
"@vitest/spy": "3.0.8",
"@vitest/utils": "3.0.8",
"@vitest/expect": "3.0.9",
"@vitest/mocker": "3.0.9",
"@vitest/pretty-format": "^3.0.9",
"@vitest/runner": "3.0.9",
"@vitest/snapshot": "3.0.9",
"@vitest/spy": "3.0.9",
"@vitest/utils": "3.0.9",
"chai": "^5.2.0",
"debug": "^4.4.0",
"expect-type": "^1.1.0",
@ -9759,7 +9759,7 @@
"tinypool": "^1.0.2",
"tinyrainbow": "^2.0.0",
"vite": "^5.0.0 || ^6.0.0",
"vite-node": "3.0.8",
"vite-node": "3.0.9",
"why-is-node-running": "^2.3.0"
},
"bin": {
@ -9775,8 +9775,8 @@
"@edge-runtime/vm": "*",
"@types/debug": "^4.1.12",
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
"@vitest/browser": "3.0.8",
"@vitest/ui": "3.0.8",
"@vitest/browser": "3.0.9",
"@vitest/ui": "3.0.9",
"happy-dom": "*",
"jsdom": "*"
},

View File

@ -1,6 +1,6 @@
{
"name": "immich-web",
"version": "1.130.2",
"version": "1.130.3",
"license": "GNU Affero General Public License version 3",
"scripts": {
"dev": "vite dev --host 0.0.0.0 --port 3000",
@ -56,7 +56,7 @@
"prettier-plugin-sort-json": "^4.1.1",
"prettier-plugin-svelte": "^3.3.3",
"rollup-plugin-visualizer": "^5.14.0",
"svelte": "^5.22.6",
"svelte": "^5.25.3",
"svelte-check": "^4.1.5",
"tailwindcss": "^3.4.17",
"tslib": "^2.6.2",

View File

@ -170,7 +170,7 @@
</div>
</form>
</div>
<TemplateSettings {defaultConfig} {config} {savedConfig} {onReset} {onSave} />
<TemplateSettings {config} {savedConfig} />
<SettingButtonsRow
onReset={(options) => onReset({ ...options, configKeys: ['notifications', 'templates'] })}

View File

@ -11,15 +11,10 @@
import { mdiEyeOutline } from '@mdi/js';
import { t } from 'svelte-i18n';
import { fade } from 'svelte/transition';
import type { SettingsResetEvent, SettingsSaveEvent } from '../admin-settings';
interface Props {
savedConfig: SystemConfigDto;
defaultConfig: SystemConfigDto;
config: SystemConfigDto;
disabled?: boolean;
onReset: SettingsResetEvent;
onSave: SettingsSaveEvent;
}
let { savedConfig, config = $bindable() }: Props = $props();

View File

@ -482,7 +482,6 @@
{preloadAssets}
onPreviousAsset={() => navigateAsset('previous')}
onNextAsset={() => navigateAsset('next')}
onClose={closeViewer}
haveFadeTransition={false}
{sharedLink}
/>
@ -527,7 +526,6 @@
{preloadAssets}
onPreviousAsset={() => navigateAsset('previous')}
onNextAsset={() => navigateAsset('next')}
onClose={closeViewer}
{sharedLink}
haveFadeTransition={$slideshowState === SlideshowState.None || $slideshowTransition}
/>

View File

@ -32,7 +32,6 @@
onNextAsset?: (() => void) | null;
copyImage?: () => Promise<void>;
zoomToggle?: (() => void) | null;
onClose?: () => void;
}
let {

View File

@ -18,10 +18,8 @@
circle?: boolean;
hidden?: boolean;
border?: boolean;
preload?: boolean;
hiddenIconClass?: string;
onComplete?: (() => void) | undefined;
onClick?: (() => void) | undefined;
}
let {

View File

@ -1,6 +1,4 @@
<script lang="ts">
import { press, tap } from 'svelte-gestures';
import Icon from '$lib/components/elements/icon.svelte';
import { ProjectionType } from '$lib/constants';
import { getAssetThumbnailUrl, isSharedLink } from '$lib/utils';
@ -74,7 +72,7 @@
IMAGE_THUMBNAIL: { THUMBHASH_FADE_DURATION },
} = TUNABLES;
let isTouchDevice = $derived(mobileDevice.hoverNone);
let usingMobileDevice = $derived(mobileDevice.pointerCoarse);
let focussableElement: HTMLElement | undefined = $state();
let mouseOver = $state(false);
let loaded = $state(false);
@ -103,10 +101,8 @@
}
onClick?.($state.snapshot(asset));
};
const handleClick = (e: MouseEvent) => {
if (isTouchDevice) {
return;
}
if (e.ctrlKey || e.metaKey) {
return;
}
@ -116,7 +112,7 @@
};
const onMouseEnter = () => {
if (isTouchDevice) {
if (usingMobileDevice) {
return;
}
mouseOver = true;
@ -126,6 +122,25 @@
const onMouseLeave = () => {
mouseOver = false;
};
function longPress(element: HTMLElement, { onLongPress }: { onLongPress: () => void }) {
let timer: ReturnType<typeof setTimeout>;
const start = (event: TouchEvent) => {
timer = setTimeout(() => {
onLongPress();
event.preventDefault();
}, 350);
};
const end = () => clearTimeout(timer);
element.addEventListener('touchstart', start);
element.addEventListener('touchend', end);
return {
destroy: () => {
element.removeEventListener('touchstart', start);
element.removeEventListener('touchend', end);
},
};
}
</script>
<div
@ -146,9 +161,6 @@
></canvas>
{/if}
<!-- svelte queries for all links on afterNavigate, leading to performance problems in asset-grid which updates
the navigation url on scroll. Replace this with button for now. -->
<!-- as of iOS17, there is a preference for long press speed, which is not available for mobile web.
The defaults are as follows:
fast: 200ms
@ -163,10 +175,7 @@
class:cursor-pointer={!disabled}
onmouseenter={onMouseEnter}
onmouseleave={onMouseLeave}
use:press={() => ({ timeframe: 350, triggerBeforeFinished: true })}
use:tap={() => ({ timeframe: 350 })}
onpress={(evt) => (evt.detail.pointerType === 'mouse' ? void 0 : onSelect?.($state.snapshot(asset)))}
ontap={(evt) => (evt.detail.pointerType === 'mouse' ? void 0 : callClickHandlers())}
use:longPress={{ onLongPress: () => onSelect?.($state.snapshot(asset)) }}
onkeydown={(evt) => {
if (evt.key === 'Enter') {
callClickHandlers();
@ -182,7 +191,7 @@
onfocus={handleFocus}
data-testid="container-with-tabindex"
>
{#if !isTouchDevice && mouseOver && !disableMouseOver}
{#if !usingMobileDevice && mouseOver && !disableMouseOver}
<!-- lazy show the url on mouse over-->
<a
class="absolute z-30 {className} top-[41px]"
@ -229,7 +238,7 @@
class:rounded-xl={selected}
>
<!-- Gradient overlay on hover -->
{#if !isTouchDevice}
{#if !usingMobileDevice}
<div
class="absolute z-10 h-full w-full bg-gradient-to-b from-black/25 via-[transparent_25%] opacity-0 transition-opacity group-hover:opacity-100"
class:rounded-xl={selected}

View File

@ -136,7 +136,7 @@
<div class="flex flex-wrap gap-1 bg-immich-bg p-2 pb-8 dark:bg-immich-dark-bg md:px-8 mt-16">
<PeopleInfiniteScroll {people} hasNextPage={true} {loadNextPage}>
{#snippet children({ person, index })}
{#snippet children({ person })}
{@const hidden = personIsHidden[person.id]}
<button
type="button"
@ -146,7 +146,6 @@
aria-label={person.name ? $t('hide_named_person', { values: { name: person.name } }) : $t('hide_person')}
>
<ImageThumbnail
preload={index < 20}
{hidden}
shadow
url={getPeopleThumbnailUrl(person)}

View File

@ -100,7 +100,6 @@
url={getPeopleThumbnailUrl(person)}
altText={person.name}
widthStyle="100%"
onClick={() => changePersonToMerge(person)}
/>
</button>
</div>

View File

@ -20,14 +20,13 @@
interface Props {
person: PersonResponseDto;
preload?: boolean;
onSetBirthDate: () => void;
onMergePeople: () => void;
onHidePerson: () => void;
onToggleFavorite: () => void;
}
let { person, preload = false, onSetBirthDate, onMergePeople, onHidePerson, onToggleFavorite }: Props = $props();
let { person, onSetBirthDate, onMergePeople, onHidePerson, onToggleFavorite }: Props = $props();
let showVerticalDots = $state(false);
</script>
@ -48,7 +47,6 @@
<div class="w-full h-full rounded-xl brightness-95 filter">
<ImageThumbnail
shadow
{preload}
url={getPeopleThumbnailUrl(person)}
altText={person.name}
title={person.name}

View File

@ -18,7 +18,7 @@
let allTags: TagResponseDto[] = $state([]);
let tagMap = $derived(Object.fromEntries(allTags.map((tag) => [tag.id, tag])));
let selectedIds = $state(new SvelteSet<string>());
let selectedIds = new SvelteSet<string>();
let disabled = $derived(selectedIds.size === 0);
let allowCreate: boolean = $state(true);

View File

@ -83,7 +83,7 @@
let bottomSectionHeight = 60;
let leadout = $state(false);
const usingMobileDevice = $derived(mobileDevice.hoverNone);
const usingMobileDevice = $derived(mobileDevice.pointerCoarse);
const scrollTo = (top: number) => {
element?.scrollTo({ top });

View File

@ -14,7 +14,6 @@
timelineBottomOffset?: number;
height?: number;
assetStore: AssetStore;
invisible?: boolean;
scrubOverallPercent?: number;
scrubBucketPercent?: number;
scrubBucket?: { bucketDate: string | undefined };
@ -268,7 +267,7 @@
globalThis.removeEventListener('touchmove', onTouchMove);
};
});
const usingMobileDevice = $derived(mobileDevice.hoverNone);
const usingMobileDevice = $derived(mobileDevice.pointerCoarse);
const width = $derived(isDragging ? '100vw' : usingMobileDevice ? '20px' : '60px');
</script>

View File

@ -13,7 +13,6 @@
flippedLogo?: boolean;
isSelected?: boolean;
preloadData?: boolean;
moreInformation?: Snippet;
dropDownContent?: Snippet;
dropdownOpen?: boolean;
}

View File

@ -24,6 +24,7 @@
const { isViewing: showAssetViewer, asset: viewingAsset, setAsset } = assetViewingStore;
const getAssetIndex = (id: string) => assets.findIndex((asset) => asset.id === id);
// eslint-disable-next-line svelte/no-unnecessary-state-wrap
let selectedAssetIds = $state(new SvelteSet<string>());
let trashCount = $derived(assets.length - selectedAssetIds.size);

View File

@ -579,6 +579,9 @@ export class AssetStore {
initTask = new CancellableTask(
() => {
this.isInitialized = true;
if (this.#options.albumId || this.#options.personId) {
return;
}
this.connect();
},
() => {

View File

@ -1,11 +1,11 @@
import { MediaQuery } from 'svelte/reactivity';
const hoverNone = new MediaQuery('hover: none');
const pointerCoarse = new MediaQuery('pointer:coarse');
const maxMd = new MediaQuery('max-width: 767px');
export const mobileDevice = {
get hoverNone() {
return hoverNone.current;
get pointerCoarse() {
return pointerCoarse.current;
},
get maxMd() {
return maxMd.current;

View File

@ -394,7 +394,7 @@
}
});
let album = $state(data.album);
let album = $derived(data.album);
let albumId = $derived(album.id);
$effect(() => {
@ -404,6 +404,7 @@
});
let assetStore = new AssetStore();
$effect(() => {
if (viewMode === AlbumPageViewMode.VIEW) {
void assetStore.updateOptions({ albumId, order: albumOrder });

View File

@ -408,13 +408,12 @@
{#if countVisiblePeople > 0 && (!searchName || searchedPeopleLocal.length > 0)}
<PeopleInfiniteScroll people={showPeople} hasNextPage={!!nextPage && !searchName} {loadNextPage}>
{#snippet children({ person, index })}
{#snippet children({ person })}
<div
class="p-2 rounded-xl hover:bg-gray-200 border-2 hover:border-immich-primary/50 hover:shadow-sm dark:hover:bg-immich-dark-primary/20 hover:dark:border-immich-dark-primary/25 border-transparent transition-all"
>
<PeopleCard
{person}
preload={index < 20}
onSetBirthDate={() => handleSetBirthDate(person)}
onMergePeople={() => handleMergePeople(person)}
onHidePerson={() => handleHidePerson(person)}