Compare commits

...

27 Commits

Author SHA1 Message Date
Alex Tran
39867906ce fix(server): metadata extraction job on missing does not include file with missing nullable field 2025-03-29 21:07:29 -05:00
Jason Rasmussen
55a3c30664 feat: kysely migrations (#17198) 2025-03-29 09:26:24 -04:00
renovate[bot]
6fa0cb534a fix(deps): update dependency @opentelemetry/context-async-hooks to v2 (#17031)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-28 20:51:01 +01:00
Ben McCann
9f0dbfc150 chore(web): update to newer persisted store package name (#17094) 2025-03-28 20:40:57 +01:00
Saschl
6419ac74af fix: update renderlist after asset deleted (#16786) 2025-03-28 18:34:19 +00:00
Yaros
d2bcf5d716 fix(mobile): pause background video play (#17032)
* fix(mobile): prevent background video playback

* fix: logic for tracking app state

* chore: move lifecycle handler in separate file

* chore: replace useState with useRef

* chore: useOnAppLifecycleStateChange

* fix: removed print statement
2025-03-28 10:32:25 -05:00
shenlong
c8331f111f fix(mobile): prefer remote orientation (#17177)
* fix(mobile): prefer remote orientation

* pr feedback

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2025-03-28 10:24:31 -05:00
Jason Rasmussen
4b4bcd23f4 feat: schema diff sql tools (#17116) 2025-03-28 10:40:09 -04:00
Ben McCann
3fde5a8328 feat: map globe view, style hot reloading and load lag fixed (#17079)
* chore: upgrade svelte-maplibre and enforce runes

* feat: maplibre-gl 5, globe view, style hot reloading, fast map markers

* fix: remove location-pin class that wasn't being used

---------

Co-authored-by: Zack Pollard <zackpollard@ymail.com>
2025-03-28 14:08:54 +00:00
Joren Guillaume
cc3ea32cd2 docs: update folder support for app in README.md (#17191)
Update folder support for app in README.md
2025-03-28 09:35:36 +00:00
Ben McCann
431cf281da chore(web): update typescript-eslint (#17093) 2025-03-28 00:04:31 -04:00
Alex
8f786fd7dd fix(web): form reactivity (#17183) 2025-03-27 19:58:49 -05:00
Alex
3e73765375 fix(web): don't show newly uploaded asset in inapplicable views (#17184) 2025-03-27 19:45:50 -04:00
Alex
411521b21d chore: post release tasks (#17179) 2025-03-27 19:41:22 -04:00
renovate[bot]
e163808348 fix(deps): update typescript-projects (#17080)
* fix(deps): update typescript-projects

* fix: otel

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2025-03-27 22:33:58 +00:00
Ben McCann
411772123f chore(web): remove unused props (#17141) 2025-03-27 23:12:14 +01:00
Mert
84c35e35d6 chore(ml): installable package (#17153)
* app -> immich_ml

* fix test ci

* omit file name

* add new line

* add new line
2025-03-27 19:49:09 +00:00
Mert
f7d730eb05 chore(ml): remove exporter (#17182)
* remove exporter code

* update gha
2025-03-27 14:48:02 -04:00
Mert
16e0166d22 docs: evaluate models on xtd-10 and flickr30k (#17159)
update docs
2025-03-27 11:30:51 -05:00
github-actions
43f8f473e9 chore: version v1.130.3 2025-03-27 15:54:30 +00:00
shenlong
cc393b2b7b fix(mobile): oauth-mobile-first-login (#17173)
invalidate ref

Co-authored-by: Alex <alex.tran1502@gmail.com>
2025-03-27 15:49:55 +00:00
Alex
6341962de4 fix(web): better touch device detection (#17144)
* fix(web): better touch device detection

* variable name
2025-03-27 10:43:56 -05:00
Min Idzelis
c26b28f6a4 fix: bug with svelte gestures (#17154)
* fix: bug with svelte gestures

* lint
2025-03-27 08:51:52 -05:00
shenlong
c72c82c401 fix(mobile): faster device album refresh after initial sync (#17170)
fix(mobile): faster device album refresh after fresh sync

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2025-03-27 08:47:05 -05:00
Alex
fecf3809a6 fix(server): album count does not account for assets without exif (#17150)
* fix(server): album count doesn't accounted for assets without exif

* sql
2025-03-26 21:24:22 -05:00
Mert
619bd72de9 docs: mention rknn among image options (#17156)
mention rknn
2025-03-26 19:05:48 -04:00
Jason Rasmussen
fd4a5f71b5 fix: broken album page (#17149) 2025-03-26 18:59:23 -04:00
238 changed files with 8440 additions and 6569 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
@@ -525,7 +525,7 @@ jobs:
- name: Generate new migrations
continue-on-error: true
run: npm run typeorm:migrations:generate ./src/migrations/TestMigration
run: npm run migrations:generate TestMigration
- name: Find file changes
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20
@@ -538,7 +538,7 @@ jobs:
run: |
echo "ERROR: Generated migration files not up to date!"
echo "Changed files: ${{ steps.verify-changed-files.outputs.changed_files }}"
cat ./src/migrations/*-TestMigration.ts
cat ./src/*-TestMigration.ts
exit 1
- name: Run SQL generation

View File

@@ -104,7 +104,7 @@ For the mobile app, you can use `https://demo.immich.app` for the `Server Endpoi
| Read-only gallery | Yes | Yes |
| Stacked Photos | Yes | Yes |
| Tags | No | Yes |
| Folder View | No | Yes |
| Folder View | Yes | Yes |
## Translations

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

@@ -263,10 +263,6 @@ class GalleryViewerPage extends HookConsumerWidget {
PhotoViewGalleryPageOptions buildAsset(BuildContext context, int index) {
var newAsset = loadAsset(index);
WidgetsBinding.instance.addPostFrameCallback((_) {
ref.read(currentAssetProvider.notifier).set(newAsset);
});
final stackId = newAsset.stackId;
if (stackId != null && currentIndex.value == index) {
final stackElements =

View File

@@ -44,6 +44,10 @@ class NativeVideoViewerPage extends HookConsumerWidget {
final lastVideoPosition = useRef(-1);
final isBuffering = useRef(false);
// Used to track whether the video should play when the app
// is brought back to the foreground
final shouldPlayOnForeground = useRef(true);
// When a video is opened through the timeline, `isCurrent` will immediately be true.
// When swiping from video A to video B, `isCurrent` will initially be true for video A and false for video B.
// If the swipe is completed, `isCurrent` will be true for video B after a delay.
@@ -368,6 +372,20 @@ class NativeVideoViewerPage extends HookConsumerWidget {
const [],
);
useOnAppLifecycleStateChange((_, state) async {
if (state == AppLifecycleState.resumed && shouldPlayOnForeground.value) {
controller.value?.play();
} else if (state == AppLifecycleState.paused) {
final videoPlaying = await controller.value?.isPlaying();
if (videoPlaying ?? true) {
shouldPlayOnForeground.value = true;
controller.value?.pause();
} else {
shouldPlayOnForeground.value = false;
}
}
});
return Stack(
children: [
// This remains under the video to avoid flickering

View File

@@ -1,5 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
@@ -389,10 +388,7 @@ class AssetService {
}
Future<double> getAspectRatio(Asset asset) async {
// platform_manager always returns 0 for orientation on iOS, so only prefer it on Android
if (asset.isLocal && Platform.isAndroid) {
await asset.localAsync;
} else if (asset.isRemote) {
if (asset.isRemote) {
asset = await loadExif(asset);
} else if (asset.isLocal) {
await asset.localAsync;

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

@@ -95,6 +95,11 @@ class BottomGalleryBar extends ConsumerWidget {
totalAssets.value -= 1;
}
if (isDeleted) {
ref
.read(currentAssetProvider.notifier)
.set(renderList.loadAsset(assetIndex.value));
}
return isDeleted;
}

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
*/

View File

@@ -77,6 +77,14 @@ export default [
],
},
],
'@typescript-eslint/no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
},
},
];

1569
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,
@@ -23,8 +23,8 @@
"test:medium": "vitest --config test/vitest.config.medium.mjs",
"typeorm": "typeorm",
"lifecycle": "node ./dist/utils/lifecycle.js",
"typeorm:migrations:create": "typeorm migration:create",
"typeorm:migrations:generate": "typeorm migration:generate -d ./dist/bin/database.js",
"migrations:generate": "node ./dist/bin/migrations.js generate",
"migrations:create": "node ./dist/bin/migrations.js create",
"typeorm:migrations:run": "typeorm migration:run -d ./dist/bin/database.js",
"typeorm:migrations:revert": "typeorm migration:revert -d ./dist/bin/database.js",
"typeorm:schema:drop": "typeorm query -d ./dist/bin/database.js 'DROP schema public cascade; CREATE schema public;'",
@@ -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/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/auto-instrumentations-node": "^0.57.0",
"@opentelemetry/context-async-hooks": "^2.0.0",
"@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

@@ -0,0 +1,135 @@
#!/usr/bin/env node
process.env.DB_URL = 'postgres://postgres:postgres@localhost:5432/immich';
import { writeFileSync } from 'node:fs';
import postgres from 'postgres';
import { ConfigRepository } from 'src/repositories/config.repository';
import 'src/schema/tables';
import { DatabaseTable, schemaDiff, schemaFromDatabase, schemaFromDecorators } from 'src/sql-tools';
const main = async () => {
const command = process.argv[2];
const name = process.argv[3] || 'Migration';
switch (command) {
case 'debug': {
await debug();
return;
}
case 'create': {
create(name, [], []);
return;
}
case 'generate': {
await generate(name);
return;
}
default: {
console.log(`Usage:
node dist/bin/migrations.js create <name>
node dist/bin/migrations.js generate <name>
`);
}
}
};
const debug = async () => {
const { up, down } = await compare();
const upSql = '-- UP\n' + up.asSql({ comments: true }).join('\n');
const downSql = '-- DOWN\n' + down.asSql({ comments: true }).join('\n');
writeFileSync('./migrations.sql', upSql + '\n\n' + downSql);
console.log('Wrote migrations.sql');
};
const generate = async (name: string) => {
const { up, down } = await compare();
if (up.items.length === 0) {
console.log('No changes detected');
return;
}
create(name, up.asSql(), down.asSql());
};
const create = (name: string, up: string[], down: string[]) => {
const timestamp = Date.now();
const filename = `${timestamp}-${name}.ts`;
const fullPath = `./src/${filename}`;
writeFileSync(fullPath, asMigration('kysely', { name, timestamp, up, down }));
console.log(`Wrote ${fullPath}`);
};
const compare = async () => {
const configRepository = new ConfigRepository();
const { database } = configRepository.getEnv();
const db = postgres(database.config.kysely);
const source = schemaFromDecorators();
const target = await schemaFromDatabase(db, {});
console.log(source.warnings.join('\n'));
const isIncluded = (table: DatabaseTable) => source.tables.some(({ name }) => table.name === name);
target.tables = target.tables.filter((table) => isIncluded(table));
const up = schemaDiff(source, target, { ignoreExtraTables: true });
const down = schemaDiff(target, source, { ignoreExtraTables: false });
return { up, down };
};
type MigrationProps = {
name: string;
timestamp: number;
up: string[];
down: string[];
};
const asMigration = (type: 'kysely' | 'typeorm', options: MigrationProps) =>
type === 'typeorm' ? asTypeOrmMigration(options) : asKyselyMigration(options);
const asTypeOrmMigration = ({ timestamp, name, up, down }: MigrationProps) => {
const upSql = up.map((sql) => ` await queryRunner.query(\`${sql}\`);`).join('\n');
const downSql = down.map((sql) => ` await queryRunner.query(\`${sql}\`);`).join('\n');
return `import { MigrationInterface, QueryRunner } from 'typeorm';
export class ${name}${timestamp} implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
${upSql}
}
public async down(queryRunner: QueryRunner): Promise<void> {
${downSql}
}
}
`;
};
const asKyselyMigration = ({ up, down }: MigrationProps) => {
const upSql = up.map((sql) => ` await sql\`${sql}\`.execute(db);`).join('\n');
const downSql = down.map((sql) => ` await sql\`${sql}\`.execute(db);`).join('\n');
return `import { Kysely, sql } from 'kysely';
export async function up(db: Kysely<any>): Promise<void> {
${upSql}
}
export async function down(db: Kysely<any>): Promise<void> {
${downSql}
}
`;
};
main()
.then(() => {
process.exit(0);
})
.catch((error) => {
console.error(error);
console.log('Something went wrong');
process.exit(1);
});

25
server/src/db.d.ts vendored
View File

@@ -4,8 +4,9 @@
*/
import type { ColumnType } from 'kysely';
import { OnThisDayData } from 'src/entities/memory.entity';
import { AssetType, MemoryType, Permission, SyncEntityType } from 'src/enum';
import { UserTable } from 'src/schema/tables/user.table';
import { OnThisDayData } from 'src/types';
export type ArrayType<T> = ArrayTypeImpl<T> extends (infer U)[] ? U[] : ArrayTypeImpl<T>;
@@ -410,26 +411,6 @@ export interface UserMetadata {
value: Json;
}
export interface Users {
createdAt: Generated<Timestamp>;
deletedAt: Timestamp | null;
email: string;
id: Generated<string>;
isAdmin: Generated<boolean>;
name: Generated<string>;
oauthId: Generated<string>;
password: Generated<string>;
profileChangedAt: Generated<Timestamp>;
profileImagePath: Generated<string>;
quotaSizeInBytes: Int8 | null;
quotaUsageInBytes: Generated<Int8>;
shouldChangePassword: Generated<boolean>;
status: Generated<string>;
storageLabel: string | null;
updatedAt: Generated<Timestamp>;
updateId: Generated<string>;
}
export interface UsersAudit {
id: Generated<string>;
userId: string;
@@ -495,7 +476,7 @@ export interface DB {
tags_closure: TagsClosure;
typeorm_metadata: TypeormMetadata;
user_metadata: UserMetadata;
users: Users;
users: UserTable;
users_audit: UsersAudit;
'vectors.pg_vector_index_stat': VectorsPgVectorIndexStat;
version_history: VersionHistory;

View File

@@ -1,55 +0,0 @@
import { AlbumEntity } from 'src/entities/album.entity';
import { AssetEntity } from 'src/entities/asset.entity';
import { UserEntity } from 'src/entities/user.entity';
import {
Check,
Column,
CreateDateColumn,
Entity,
Index,
ManyToOne,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
@Entity('activity')
@Index('IDX_activity_like', ['assetId', 'userId', 'albumId'], { unique: true, where: '("isLiked" = true)' })
@Check(`("comment" IS NULL AND "isLiked" = true) OR ("comment" IS NOT NULL AND "isLiked" = false)`)
export class ActivityEntity {
@PrimaryGeneratedColumn('uuid')
id!: string;
@CreateDateColumn({ type: 'timestamptz' })
createdAt!: Date;
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt!: Date;
@Index('IDX_activity_update_id')
@Column({ type: 'uuid', nullable: false, default: () => 'immich_uuid_v7()' })
updateId?: string;
@Column()
albumId!: string;
@Column()
userId!: string;
@Column({ nullable: true, type: 'uuid' })
assetId!: string | null;
@Column({ type: 'text', default: null })
comment!: string | null;
@Column({ type: 'boolean', default: false })
isLiked!: boolean;
@ManyToOne(() => AssetEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: true })
asset!: AssetEntity | null;
@ManyToOne(() => UserEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
user!: UserEntity;
@ManyToOne(() => AlbumEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
album!: AlbumEntity;
}

View File

@@ -1,27 +1,11 @@
import { AlbumEntity } from 'src/entities/album.entity';
import { UserEntity } from 'src/entities/user.entity';
import { AlbumUserRole } from 'src/enum';
import { Column, Entity, Index, JoinColumn, ManyToOne, PrimaryColumn } from 'typeorm';
@Entity('albums_shared_users_users')
// Pre-existing indices from original album <--> user ManyToMany mapping
@Index('IDX_427c350ad49bd3935a50baab73', ['album'])
@Index('IDX_f48513bf9bccefd6ff3ad30bd0', ['user'])
export class AlbumUserEntity {
@PrimaryColumn({ type: 'uuid', name: 'albumsId' })
albumId!: string;
@PrimaryColumn({ type: 'uuid', name: 'usersId' })
userId!: string;
@JoinColumn({ name: 'albumsId' })
@ManyToOne(() => AlbumEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false })
album!: AlbumEntity;
@JoinColumn({ name: 'usersId' })
@ManyToOne(() => UserEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false })
user!: UserEntity;
@Column({ type: 'varchar', default: AlbumUserRole.EDITOR })
role!: AlbumUserRole;
}

View File

@@ -3,69 +3,22 @@ import { AssetEntity } from 'src/entities/asset.entity';
import { SharedLinkEntity } from 'src/entities/shared-link.entity';
import { UserEntity } from 'src/entities/user.entity';
import { AssetOrder } from 'src/enum';
import {
Column,
CreateDateColumn,
DeleteDateColumn,
Entity,
Index,
JoinTable,
ManyToMany,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
@Entity('albums')
export class AlbumEntity {
@PrimaryGeneratedColumn('uuid')
id!: string;
@ManyToOne(() => UserEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false })
owner!: UserEntity;
@Column()
ownerId!: string;
@Column({ default: 'Untitled Album' })
albumName!: string;
@Column({ type: 'text', default: '' })
description!: string;
@CreateDateColumn({ type: 'timestamptz' })
createdAt!: Date;
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt!: Date;
@Index('IDX_albums_update_id')
@Column({ type: 'uuid', nullable: false, default: () => 'immich_uuid_v7()' })
updateId?: string;
@DeleteDateColumn({ type: 'timestamptz' })
deletedAt!: Date | null;
@ManyToOne(() => AssetEntity, { nullable: true, onDelete: 'SET NULL', onUpdate: 'CASCADE' })
albumThumbnailAsset!: AssetEntity | null;
@Column({ comment: 'Asset ID to be used as thumbnail', nullable: true })
albumThumbnailAssetId!: string | null;
@OneToMany(() => AlbumUserEntity, ({ album }) => album, { cascade: true, onDelete: 'CASCADE' })
albumUsers!: AlbumUserEntity[];
@ManyToMany(() => AssetEntity, (asset) => asset.albums)
@JoinTable({ synchronize: false })
assets!: AssetEntity[];
@OneToMany(() => SharedLinkEntity, (link) => link.album)
sharedLinks!: SharedLinkEntity[];
@Column({ default: true })
isActivityEnabled!: boolean;
@Column({ type: 'varchar', default: AssetOrder.DESC })
order!: AssetOrder;
}

View File

@@ -1,34 +0,0 @@
import { UserEntity } from 'src/entities/user.entity';
import { Permission } from 'src/enum';
import { Column, CreateDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
@Entity('api_keys')
export class APIKeyEntity {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Column()
name!: string;
@Column({ select: false })
key?: string;
@ManyToOne(() => UserEntity, { onUpdate: 'CASCADE', onDelete: 'CASCADE' })
user?: UserEntity;
@Column()
userId!: string;
@Column({ array: true, type: 'varchar' })
permissions!: Permission[];
@CreateDateColumn({ type: 'timestamptz' })
createdAt!: Date;
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt!: Date;
@Index('IDX_api_keys_update_id')
@Column({ type: 'uuid', nullable: false, default: () => 'immich_uuid_v7()' })
updateId?: string;
}

View File

@@ -1,19 +0,0 @@
import { Column, CreateDateColumn, Entity, Index, PrimaryColumn } from 'typeorm';
@Entity('assets_audit')
export class AssetAuditEntity {
@PrimaryColumn({ type: 'uuid', nullable: false, default: () => 'immich_uuid_v7()' })
id!: string;
@Index('IDX_assets_audit_asset_id')
@Column({ type: 'uuid' })
assetId!: string;
@Index('IDX_assets_audit_owner_id')
@Column({ type: 'uuid' })
ownerId!: string;
@Index('IDX_assets_audit_deleted_at')
@CreateDateColumn({ type: 'timestamptz', default: () => 'clock_timestamp()' })
deletedAt!: Date;
}

View File

@@ -2,55 +2,20 @@ import { AssetEntity } from 'src/entities/asset.entity';
import { FaceSearchEntity } from 'src/entities/face-search.entity';
import { PersonEntity } from 'src/entities/person.entity';
import { SourceType } from 'src/enum';
import { Column, Entity, Index, ManyToOne, OneToOne, PrimaryGeneratedColumn } from 'typeorm';
@Entity('asset_faces', { synchronize: false })
@Index('IDX_asset_faces_assetId_personId', ['assetId', 'personId'])
@Index(['personId', 'assetId'])
export class AssetFaceEntity {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Column()
assetId!: string;
@Column({ nullable: true, type: 'uuid' })
personId!: string | null;
@OneToOne(() => FaceSearchEntity, (faceSearchEntity) => faceSearchEntity.face, { cascade: ['insert'] })
faceSearch?: FaceSearchEntity;
@Column({ default: 0, type: 'int' })
imageWidth!: number;
@Column({ default: 0, type: 'int' })
imageHeight!: number;
@Column({ default: 0, type: 'int' })
boundingBoxX1!: number;
@Column({ default: 0, type: 'int' })
boundingBoxY1!: number;
@Column({ default: 0, type: 'int' })
boundingBoxX2!: number;
@Column({ default: 0, type: 'int' })
boundingBoxY2!: number;
@Column({ default: SourceType.MACHINE_LEARNING, type: 'enum', enum: SourceType })
sourceType!: SourceType;
@ManyToOne(() => AssetEntity, (asset) => asset.faces, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
asset!: AssetEntity;
@ManyToOne(() => PersonEntity, (person) => person.faces, {
onDelete: 'SET NULL',
onUpdate: 'CASCADE',
nullable: true,
})
person!: PersonEntity | null;
@Column({ type: 'timestamptz' })
deletedAt!: Date | null;
}

View File

@@ -1,42 +1,13 @@
import { AssetEntity } from 'src/entities/asset.entity';
import { AssetFileType } from 'src/enum';
import {
Column,
CreateDateColumn,
Entity,
Index,
ManyToOne,
PrimaryGeneratedColumn,
Unique,
UpdateDateColumn,
} from 'typeorm';
@Unique('UQ_assetId_type', ['assetId', 'type'])
@Entity('asset_files')
export class AssetFileEntity {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Index('IDX_asset_files_assetId')
@Column()
assetId!: string;
@ManyToOne(() => AssetEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
asset?: AssetEntity;
@CreateDateColumn({ type: 'timestamptz' })
createdAt!: Date;
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt!: Date;
@Index('IDX_asset_files_update_id')
@Column({ type: 'uuid', nullable: false, default: () => 'immich_uuid_v7()' })
updateId?: string;
@Column()
type!: AssetFileType;
@Column()
path!: string;
}

View File

@@ -1,27 +1,11 @@
import { AssetEntity } from 'src/entities/asset.entity';
import { Column, Entity, JoinColumn, OneToOne, PrimaryColumn } from 'typeorm';
@Entity('asset_job_status')
export class AssetJobStatusEntity {
@OneToOne(() => AssetEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
@JoinColumn()
asset!: AssetEntity;
@PrimaryColumn()
assetId!: string;
@Column({ type: 'timestamptz', nullable: true })
facesRecognizedAt!: Date | null;
@Column({ type: 'timestamptz', nullable: true })
metadataExtractedAt!: Date | null;
@Column({ type: 'timestamptz', nullable: true })
duplicatesDetectedAt!: Date | null;
@Column({ type: 'timestamptz', nullable: true })
previewAt!: Date | null;
@Column({ type: 'timestamptz', nullable: true })
thumbnailAt!: Date | null;
}

View File

@@ -6,7 +6,6 @@ import { AssetFaceEntity } from 'src/entities/asset-face.entity';
import { AssetFileEntity } from 'src/entities/asset-files.entity';
import { AssetJobStatusEntity } from 'src/entities/asset-job-status.entity';
import { ExifEntity } from 'src/entities/exif.entity';
import { LibraryEntity } from 'src/entities/library.entity';
import { SharedLinkEntity } from 'src/entities/shared-link.entity';
import { SmartSearchEntity } from 'src/entities/smart-search.entity';
import { StackEntity } from 'src/entities/stack.entity';
@@ -16,171 +15,49 @@ import { AssetFileType, AssetStatus, AssetType } from 'src/enum';
import { TimeBucketSize } from 'src/repositories/asset.repository';
import { AssetSearchBuilderOptions } from 'src/repositories/search.repository';
import { anyUuid, asUuid } from 'src/utils/database';
import {
Column,
CreateDateColumn,
DeleteDateColumn,
Entity,
Index,
JoinColumn,
JoinTable,
ManyToMany,
ManyToOne,
OneToMany,
OneToOne,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
export const ASSET_CHECKSUM_CONSTRAINT = 'UQ_assets_owner_checksum';
@Entity('assets')
// Checksums must be unique per user and library
@Index(ASSET_CHECKSUM_CONSTRAINT, ['owner', 'checksum'], {
unique: true,
where: '"libraryId" IS NULL',
})
@Index('UQ_assets_owner_library_checksum' + '', ['owner', 'library', 'checksum'], {
unique: true,
where: '"libraryId" IS NOT NULL',
})
@Index('idx_local_date_time', { synchronize: false })
@Index('idx_local_date_time_month', { synchronize: false })
@Index('IDX_originalPath_libraryId', ['originalPath', 'libraryId'])
@Index('IDX_asset_id_stackId', ['id', 'stackId'])
@Index('idx_originalFileName_trigram', { synchronize: false })
// For all assets, each originalpath must be unique per user and library
export class AssetEntity {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Column()
deviceAssetId!: string;
@ManyToOne(() => UserEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false })
owner!: UserEntity;
@Column()
ownerId!: string;
@ManyToOne(() => LibraryEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
library?: LibraryEntity | null;
@Column({ nullable: true })
libraryId?: string | null;
@Column()
deviceId!: string;
@Column()
type!: AssetType;
@Column({ type: 'enum', enum: AssetStatus, default: AssetStatus.ACTIVE })
status!: AssetStatus;
@Column()
originalPath!: string;
@OneToMany(() => AssetFileEntity, (assetFile) => assetFile.asset)
files!: AssetFileEntity[];
@Column({ type: 'bytea', nullable: true })
thumbhash!: Buffer | null;
@Column({ type: 'varchar', nullable: true, default: '' })
encodedVideoPath!: string | null;
@CreateDateColumn({ type: 'timestamptz' })
createdAt!: Date;
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt!: Date;
@Index('IDX_assets_update_id')
@Column({ type: 'uuid', nullable: false, default: () => 'immich_uuid_v7()' })
updateId?: string;
@DeleteDateColumn({ type: 'timestamptz', nullable: true })
deletedAt!: Date | null;
@Index('idx_asset_file_created_at')
@Column({ type: 'timestamptz', nullable: true, default: null })
fileCreatedAt!: Date;
@Column({ type: 'timestamptz', nullable: true, default: null })
localDateTime!: Date;
@Column({ type: 'timestamptz', nullable: true, default: null })
fileModifiedAt!: Date;
@Column({ type: 'boolean', default: false })
isFavorite!: boolean;
@Column({ type: 'boolean', default: false })
isArchived!: boolean;
@Column({ type: 'boolean', default: false })
isExternal!: boolean;
@Column({ type: 'boolean', default: false })
isOffline!: boolean;
@Column({ type: 'bytea' })
@Index()
checksum!: Buffer; // sha1 checksum
@Column({ type: 'varchar', nullable: true })
duration!: string | null;
@Column({ type: 'boolean', default: true })
isVisible!: boolean;
@ManyToOne(() => AssetEntity, { nullable: true, onUpdate: 'CASCADE', onDelete: 'SET NULL' })
@JoinColumn()
livePhotoVideo!: AssetEntity | null;
@Column({ nullable: true })
livePhotoVideoId!: string | null;
@Column({ type: 'varchar' })
@Index()
originalFileName!: string;
@Column({ type: 'varchar', nullable: true })
sidecarPath!: string | null;
@OneToOne(() => ExifEntity, (exifEntity) => exifEntity.asset)
exifInfo?: ExifEntity;
@OneToOne(() => SmartSearchEntity, (smartSearchEntity) => smartSearchEntity.asset)
smartSearch?: SmartSearchEntity;
@ManyToMany(() => TagEntity, (tag) => tag.assets, { cascade: true })
@JoinTable({ name: 'tag_asset', synchronize: false })
tags!: TagEntity[];
@ManyToMany(() => SharedLinkEntity, (link) => link.assets, { cascade: true })
@JoinTable({ name: 'shared_link__asset' })
sharedLinks!: SharedLinkEntity[];
@ManyToMany(() => AlbumEntity, (album) => album.assets, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
albums?: AlbumEntity[];
@OneToMany(() => AssetFaceEntity, (assetFace) => assetFace.asset)
faces!: AssetFaceEntity[];
@Column({ nullable: true })
stackId?: string | null;
@ManyToOne(() => StackEntity, { nullable: true, onDelete: 'SET NULL', onUpdate: 'CASCADE' })
@JoinColumn()
stack?: StackEntity | null;
@OneToOne(() => AssetJobStatusEntity, (jobStatus) => jobStatus.asset, { nullable: true })
jobStatus?: AssetJobStatusEntity;
@Index('IDX_assets_duplicateId')
@Column({ type: 'uuid', nullable: true })
duplicateId!: string | null;
}

View File

@@ -1,24 +0,0 @@
import { DatabaseAction, EntityType } from 'src/enum';
import { Column, CreateDateColumn, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
@Entity('audit')
@Index('IDX_ownerId_createdAt', ['ownerId', 'createdAt'])
export class AuditEntity {
@PrimaryGeneratedColumn('increment')
id!: number;
@Column()
entityType!: EntityType;
@Column({ type: 'uuid' })
entityId!: string;
@Column()
action!: DatabaseAction;
@Column({ type: 'uuid' })
ownerId!: string;
@CreateDateColumn({ type: 'timestamptz' })
createdAt!: Date;
}

View File

@@ -1,111 +1,36 @@
import { AssetEntity } from 'src/entities/asset.entity';
import { Index, JoinColumn, OneToOne, PrimaryColumn, UpdateDateColumn } from 'typeorm';
import { Column } from 'typeorm/decorator/columns/Column.js';
import { Entity } from 'typeorm/decorator/entity/Entity.js';
@Entity('exif')
export class ExifEntity {
@OneToOne(() => AssetEntity, { onDelete: 'CASCADE', nullable: true })
@JoinColumn()
asset?: AssetEntity;
@PrimaryColumn()
assetId!: string;
@UpdateDateColumn({ type: 'timestamptz', default: () => 'clock_timestamp()' })
updatedAt?: Date;
@Index('IDX_asset_exif_update_id')
@Column({ type: 'uuid', nullable: false, default: () => 'immich_uuid_v7()' })
updateId?: string;
/* General info */
@Column({ type: 'text', default: '' })
description!: string; // or caption
@Column({ type: 'integer', nullable: true })
exifImageWidth!: number | null;
@Column({ type: 'integer', nullable: true })
exifImageHeight!: number | null;
@Column({ type: 'bigint', nullable: true })
fileSizeInByte!: number | null;
@Column({ type: 'varchar', nullable: true })
orientation!: string | null;
@Column({ type: 'timestamptz', nullable: true })
dateTimeOriginal!: Date | null;
@Column({ type: 'timestamptz', nullable: true })
modifyDate!: Date | null;
@Column({ type: 'varchar', nullable: true })
timeZone!: string | null;
@Column({ type: 'float', nullable: true })
latitude!: number | null;
@Column({ type: 'float', nullable: true })
longitude!: number | null;
@Column({ type: 'varchar', nullable: true })
projectionType!: string | null;
@Index('exif_city')
@Column({ type: 'varchar', nullable: true })
city!: string | null;
@Index('IDX_live_photo_cid')
@Column({ type: 'varchar', nullable: true })
livePhotoCID!: string | null;
@Index('IDX_auto_stack_id')
@Column({ type: 'varchar', nullable: true })
autoStackId!: string | null;
@Column({ type: 'varchar', nullable: true })
state!: string | null;
@Column({ type: 'varchar', nullable: true })
country!: string | null;
/* Image info */
@Column({ type: 'varchar', nullable: true })
make!: string | null;
@Column({ type: 'varchar', nullable: true })
model!: string | null;
@Column({ type: 'varchar', nullable: true })
lensModel!: string | null;
@Column({ type: 'float8', nullable: true })
fNumber!: number | null;
@Column({ type: 'float8', nullable: true })
focalLength!: number | null;
@Column({ type: 'integer', nullable: true })
iso!: number | null;
@Column({ type: 'varchar', nullable: true })
exposureTime!: string | null;
@Column({ type: 'varchar', nullable: true })
profileDescription!: string | null;
@Column({ type: 'varchar', nullable: true })
colorspace!: string | null;
@Column({ type: 'integer', nullable: true })
bitsPerSample!: number | null;
@Column({ type: 'integer', nullable: true })
rating!: number | null;
/* Video info */
@Column({ type: 'float8', nullable: true })
fps?: number | null;
}

View File

@@ -1,16 +1,7 @@
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
import { Column, Entity, Index, JoinColumn, OneToOne, PrimaryColumn } from 'typeorm';
@Entity('face_search', { synchronize: false })
export class FaceSearchEntity {
@OneToOne(() => AssetFaceEntity, { onDelete: 'CASCADE', nullable: true })
@JoinColumn({ name: 'faceId', referencedColumnName: 'id' })
face?: AssetFaceEntity;
@PrimaryColumn()
faceId!: string;
@Index('face_index', { synchronize: false })
@Column({ type: 'float4', array: true })
embedding!: string;
}

View File

@@ -1,73 +1,13 @@
import { Column, Entity, PrimaryColumn } from 'typeorm';
@Entity('geodata_places', { synchronize: false })
export class GeodataPlacesEntity {
@PrimaryColumn({ type: 'integer' })
id!: number;
@Column({ type: 'varchar', length: 200 })
name!: string;
@Column({ type: 'float' })
longitude!: number;
@Column({ type: 'float' })
latitude!: number;
@Column({ type: 'char', length: 2 })
countryCode!: string;
@Column({ type: 'varchar', length: 20, nullable: true })
admin1Code!: string;
@Column({ type: 'varchar', length: 80, nullable: true })
admin2Code!: string;
@Column({ type: 'varchar', nullable: true })
admin1Name!: string;
@Column({ type: 'varchar', nullable: true })
admin2Name!: string;
@Column({ type: 'varchar', nullable: true })
alternateNames!: string;
@Column({ type: 'date' })
modificationDate!: Date;
}
@Entity('geodata_places_tmp', { synchronize: false })
export class GeodataPlacesTempEntity {
@PrimaryColumn({ type: 'integer' })
id!: number;
@Column({ type: 'varchar', length: 200 })
name!: string;
@Column({ type: 'float' })
longitude!: number;
@Column({ type: 'float' })
latitude!: number;
@Column({ type: 'char', length: 2 })
countryCode!: string;
@Column({ type: 'varchar', length: 20, nullable: true })
admin1Code!: string;
@Column({ type: 'varchar', length: 80, nullable: true })
admin2Code!: string;
@Column({ type: 'varchar', nullable: true })
admin1Name!: string;
@Column({ type: 'varchar', nullable: true })
admin2Name!: string;
@Column({ type: 'varchar', nullable: true })
alternateNames!: string;
@Column({ type: 'date' })
modificationDate!: Date;
}

View File

@@ -1,55 +0,0 @@
import { AssetEntity } from 'src/entities/asset.entity';
import { UserEntity } from 'src/entities/user.entity';
import {
Column,
CreateDateColumn,
DeleteDateColumn,
Entity,
Index,
JoinTable,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
@Entity('libraries')
export class LibraryEntity {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Column()
name!: string;
@OneToMany(() => AssetEntity, (asset) => asset.library)
@JoinTable()
assets!: AssetEntity[];
@ManyToOne(() => UserEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false })
owner?: UserEntity;
@Column()
ownerId!: string;
@Column('text', { array: true })
importPaths!: string[];
@Column('text', { array: true })
exclusionPatterns!: string[];
@CreateDateColumn({ type: 'timestamptz' })
createdAt!: Date;
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt!: Date;
@Index('IDX_libraries_update_id')
@Column({ type: 'uuid', nullable: false, default: () => 'immich_uuid_v7()' })
updateId?: string;
@DeleteDateColumn({ type: 'timestamptz' })
deletedAt?: Date;
@Column({ type: 'timestamptz', nullable: true })
refreshedAt!: Date | null;
}

View File

@@ -1,74 +0,0 @@
import { AssetEntity } from 'src/entities/asset.entity';
import { UserEntity } from 'src/entities/user.entity';
import { MemoryType } from 'src/enum';
import {
Column,
CreateDateColumn,
DeleteDateColumn,
Entity,
Index,
JoinTable,
ManyToMany,
ManyToOne,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
export type OnThisDayData = { year: number };
export interface MemoryData {
[MemoryType.ON_THIS_DAY]: OnThisDayData;
}
@Entity('memories')
export class MemoryEntity<T extends MemoryType = MemoryType> {
@PrimaryGeneratedColumn('uuid')
id!: string;
@CreateDateColumn({ type: 'timestamptz' })
createdAt!: Date;
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt!: Date;
@Index('IDX_memories_update_id')
@Column({ type: 'uuid', nullable: false, default: () => 'immich_uuid_v7()' })
updateId?: string;
@DeleteDateColumn({ type: 'timestamptz' })
deletedAt?: Date;
@ManyToOne(() => UserEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false })
owner!: UserEntity;
@Column()
ownerId!: string;
@Column()
type!: T;
@Column({ type: 'jsonb' })
data!: MemoryData[T];
/** unless set to true, will be automatically deleted in the future */
@Column({ default: false })
isSaved!: boolean;
/** memories are sorted in ascending order by this value */
@Column({ type: 'timestamptz' })
memoryAt!: Date;
@Column({ type: 'timestamptz', nullable: true })
showAt?: Date;
@Column({ type: 'timestamptz', nullable: true })
hideAt?: Date;
/** when the user last viewed the memory */
@Column({ type: 'timestamptz', nullable: true })
seenAt?: Date;
@ManyToMany(() => AssetEntity)
@JoinTable()
assets!: AssetEntity[];
}

View File

@@ -1,24 +1,9 @@
import { PathType } from 'src/enum';
import { Column, Entity, PrimaryGeneratedColumn, Unique } from 'typeorm';
@Entity('move_history')
// path lock (per entity)
@Unique('UQ_entityId_pathType', ['entityId', 'pathType'])
// new path lock (global)
@Unique('UQ_newPath', ['newPath'])
export class MoveEntity {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Column({ type: 'uuid' })
entityId!: string;
@Column({ type: 'varchar' })
pathType!: PathType;
@Column({ type: 'varchar' })
oldPath!: string;
@Column({ type: 'varchar' })
newPath!: string;
}

View File

@@ -1,37 +1,7 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity('naturalearth_countries', { synchronize: false })
export class NaturalEarthCountriesEntity {
@PrimaryGeneratedColumn('identity', { generatedIdentity: 'ALWAYS' })
id!: number;
@Column({ type: 'varchar', length: 50 })
admin!: string;
@Column({ type: 'varchar', length: 3 })
admin_a3!: string;
@Column({ type: 'varchar', length: 50 })
type!: string;
@Column({ type: 'polygon' })
coordinates!: string;
}
@Entity('naturalearth_countries_tmp', { synchronize: false })
export class NaturalEarthCountriesTempEntity {
@PrimaryGeneratedColumn('identity', { generatedIdentity: 'ALWAYS' })
id!: number;
@Column({ type: 'varchar', length: 50 })
admin!: string;
@Column({ type: 'varchar', length: 3 })
admin_a3!: string;
@Column({ type: 'varchar', length: 50 })
type!: string;
@Column({ type: 'polygon' })
coordinates!: string;
}

View File

@@ -1,19 +0,0 @@
import { Column, CreateDateColumn, Entity, Index, PrimaryColumn } from 'typeorm';
@Entity('partners_audit')
export class PartnerAuditEntity {
@PrimaryColumn({ type: 'uuid', nullable: false, default: () => 'immich_uuid_v7()' })
id!: string;
@Index('IDX_partners_audit_shared_by_id')
@Column({ type: 'uuid' })
sharedById!: string;
@Index('IDX_partners_audit_shared_with_id')
@Column({ type: 'uuid' })
sharedWithId!: string;
@Index('IDX_partners_audit_deleted_at')
@CreateDateColumn({ type: 'timestamptz', default: () => 'clock_timestamp()' })
deletedAt!: Date;
}

View File

@@ -1,42 +0,0 @@
import { UserEntity } from 'src/entities/user.entity';
import {
Column,
CreateDateColumn,
Entity,
Index,
JoinColumn,
ManyToOne,
PrimaryColumn,
UpdateDateColumn,
} from 'typeorm';
/** @deprecated delete after coming up with a migration workflow for kysely */
@Entity('partners')
export class PartnerEntity {
@PrimaryColumn('uuid')
sharedById!: string;
@PrimaryColumn('uuid')
sharedWithId!: string;
@ManyToOne(() => UserEntity, { onDelete: 'CASCADE', eager: true })
@JoinColumn({ name: 'sharedById' })
sharedBy!: UserEntity;
@ManyToOne(() => UserEntity, { onDelete: 'CASCADE', eager: true })
@JoinColumn({ name: 'sharedWithId' })
sharedWith!: UserEntity;
@CreateDateColumn({ type: 'timestamptz' })
createdAt!: Date;
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt!: Date;
@Index('IDX_partners_update_id')
@Column({ type: 'uuid', nullable: false, default: () => 'immich_uuid_v7()' })
updateId?: string;
@Column({ type: 'boolean', default: false })
inTimeline!: boolean;
}

View File

@@ -1,63 +1,20 @@
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
import { UserEntity } from 'src/entities/user.entity';
import {
Check,
Column,
CreateDateColumn,
Entity,
Index,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
@Entity('person')
@Check(`"birthDate" <= CURRENT_DATE`)
export class PersonEntity {
@PrimaryGeneratedColumn('uuid')
id!: string;
@CreateDateColumn({ type: 'timestamptz' })
createdAt!: Date;
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt!: Date;
@Index('IDX_person_update_id')
@Column({ type: 'uuid', nullable: false, default: () => 'immich_uuid_v7()' })
updateId?: string;
@Column()
ownerId!: string;
@ManyToOne(() => UserEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: false })
owner!: UserEntity;
@Column({ default: '' })
name!: string;
@Column({ type: 'date', nullable: true })
birthDate!: Date | string | null;
@Column({ default: '' })
thumbnailPath!: string;
@Column({ nullable: true })
faceAssetId!: string | null;
@ManyToOne(() => AssetFaceEntity, { onDelete: 'SET NULL', nullable: true })
faceAsset!: AssetFaceEntity | null;
@OneToMany(() => AssetFaceEntity, (assetFace) => assetFace.person)
faces!: AssetFaceEntity[];
@Column({ default: false })
isHidden!: boolean;
@Column({ default: false })
isFavorite!: boolean;
@Column({ type: 'varchar', nullable: true, default: null })
color?: string | null;
}

View File

@@ -1,36 +1,16 @@
import { ExpressionBuilder } from 'kysely';
import { DB } from 'src/db';
import { UserEntity } from 'src/entities/user.entity';
import { Column, CreateDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
@Entity('sessions')
export class SessionEntity {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Column({ select: false })
token!: string;
@Column()
userId!: string;
@ManyToOne(() => UserEntity, { onUpdate: 'CASCADE', onDelete: 'CASCADE' })
user!: UserEntity;
@CreateDateColumn({ type: 'timestamptz' })
createdAt!: Date;
@UpdateDateColumn({ type: 'timestamptz' })
updatedAt!: Date;
@Index('IDX_sessions_update_id')
@Column({ type: 'uuid', nullable: false, default: () => 'immich_uuid_v7()' })
updateId!: string;
@Column({ default: '' })
deviceType!: string;
@Column({ default: '' })
deviceOS!: string;
}

View File

@@ -2,64 +2,21 @@ import { AlbumEntity } from 'src/entities/album.entity';
import { AssetEntity } from 'src/entities/asset.entity';
import { UserEntity } from 'src/entities/user.entity';
import { SharedLinkType } from 'src/enum';
import {
Column,
CreateDateColumn,
Entity,
Index,
ManyToMany,
ManyToOne,
PrimaryGeneratedColumn,
Unique,
} from 'typeorm';
@Entity('shared_links')
@Unique('UQ_sharedlink_key', ['key'])
export class SharedLinkEntity {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Column({ type: 'varchar', nullable: true })
description!: string | null;
@Column({ type: 'varchar', nullable: true })
password!: string | null;
@Column()
userId!: string;
@ManyToOne(() => UserEntity, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
user!: UserEntity;
@Index('IDX_sharedlink_key')
@Column({ type: 'bytea' })
key!: Buffer; // use to access the inidividual asset
@Column()
type!: SharedLinkType;
@CreateDateColumn({ type: 'timestamptz' })
createdAt!: Date;
@Column({ type: 'timestamptz', nullable: true })
expiresAt!: Date | null;
@Column({ type: 'boolean', default: false })
allowUpload!: boolean;
@Column({ type: 'boolean', default: true })
allowDownload!: boolean;
@Column({ type: 'boolean', default: true })
showExif!: boolean;
@ManyToMany(() => AssetEntity, (asset) => asset.sharedLinks, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
assets!: AssetEntity[];
@Index('IDX_sharedlink_albumId')
@ManyToOne(() => AlbumEntity, (album) => album.sharedLinks, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
album?: AlbumEntity;
@Column({ type: 'varchar', nullable: true })
albumId!: string | null;
}

View File

@@ -1,16 +1,7 @@
import { AssetEntity } from 'src/entities/asset.entity';
import { Column, Entity, Index, JoinColumn, OneToOne, PrimaryColumn } from 'typeorm';
@Entity('smart_search', { synchronize: false })
export class SmartSearchEntity {
@OneToOne(() => AssetEntity, { onDelete: 'CASCADE', nullable: true })
@JoinColumn({ name: 'assetId', referencedColumnName: 'id' })
asset?: AssetEntity;
@PrimaryColumn()
assetId!: string;
@Index('clip_index', { synchronize: false })
@Column({ type: 'float4', array: true })
embedding!: string;
}

Some files were not shown because too many files have changed in this diff Show More