git: 9ad2b8676f01 - main - www/immich-ml: Add New Port

Jochen Neumeister <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.ports
Message-ID <[email protected]>
The branch main has been updated by joneum:

URL: https://cgit.FreeBSD.org/ports/commit/?id=9ad2b8676f01e5368d93550b8304ad8d552b0cde

commit 9ad2b8676f01e5368d93550b8304ad8d552b0cde
Author:     Jochen Neumeister <[email protected]>
AuthorDate: 2026-08-16 17:59:55 +0000
Commit:     Jochen Neumeister <[email protected]>
CommitDate: 2026-08-16 18:03:16 +0000

    www/immich-ml: Add New Port
    
    Machine-learning service for Immich providing smart search (CLIP),
    duplicate detection and facial recognition via onnxruntime on the
    CPU. Models are downloaded on first use into the cache directory.
    OCR is not yet available as rapidocr has no FreeBSD port.
    
    WWW: https://immich.app/
    
    Sponsored by:   Netzkommune GmbH
---
 www/Makefile                                       |  1 +
 www/immich-ml/Makefile                             | 57 +++++++++++++++++++
 www/immich-ml/distinfo                             |  3 +
 www/immich-ml/files/immich_ml.in                   | 65 ++++++++++++++++++++++
 .../files/patch-immich__ml_models_____init____.py  | 31 +++++++++++
 www/immich-ml/pkg-descr                            | 10 ++++
 6 files changed, 167 insertions(+)

diff --git a/www/Makefile b/www/Makefile
index 745bd04552eb..b807784824d2 100644
--- a/www/Makefile
+++ b/www/Makefile
@@ -311,6 +311,7 @@
     SUBDIR += ilias
     SUBDIR += imaginary
     SUBDIR += immich
+    SUBDIR += immich-ml
     SUBDIR += iocaine
     SUBDIR += iridium
     SUBDIR += janus
diff --git a/www/immich-ml/Makefile b/www/immich-ml/Makefile
new file mode 100644
index 000000000000..3d9a1057d3b2
--- /dev/null
+++ b/www/immich-ml/Makefile
@@ -0,0 +1,57 @@
+PORTNAME=	immich-ml
+DISTVERSIONPREFIX=	v
+DISTVERSION=	3.1.0
+CATEGORIES=	www python
+
+MAINTAINER=	[email protected]
+COMMENT=	Machine-learning service for Immich
+WWW=		https://immich.app/
+
+LICENSE=	AGPLv3
+LICENSE_FILE=	${WRKSRC:H}/LICENSE
+
+BUILD_DEPENDS=	${PYTHON_PKGNAMEPREFIX}hatchling>0:devel/py-hatchling@${PY_FLAVOR}
+RUN_DEPENDS=	${PYTHON_PKGNAMEPREFIX}aiocache>0:devel/py-aiocache@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}fastapi>0:www/py-fastapi@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}ftfy>0:textproc/py-ftfy@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}gunicorn>0:www/py-gunicorn@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}huggingface-hub>0:misc/py-huggingface-hub@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}insightface>0:misc/py-insightface@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}ml-dtypes>0:math/py-ml-dtypes@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}numpy>0:math/py-numpy@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}onnxruntime>0:misc/py-onnxruntime@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}orjson>0:devel/py-orjson@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}pillow>0:graphics/py-pillow@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}pydantic-settings>0:devel/py-pydantic-settings@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}pydantic2>0:devel/py-pydantic2@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}python-multipart>0:www/py-python-multipart@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}rich>0:textproc/py-rich@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}tokenizers>0:textproc/py-tokenizers@${PY_FLAVOR} \
+		${PYTHON_PKGNAMEPREFIX}uvicorn>0:www/py-uvicorn@${PY_FLAVOR} \
+		opencv>0:graphics/opencv
+
+USES=		python
+
+USE_GITHUB=	yes
+GH_ACCOUNT=	immich-app
+GH_PROJECT=	immich
+
+USE_PYTHON=	autoplist pep517
+USE_RC_SUBR=	immich_ml
+
+SUB_LIST=	PYTHON_CMD=${PYTHON_CMD}
+
+WRKSRC_SUBDIR=	machine-learning
+
+USERS=		immich
+GROUPS=		immich
+
+post-patch:
+	@${REINPLACE_CMD} -e "s|uvicorn\[standard\]|uvicorn|" \
+		-e "/opencv-python-headless/d" \
+		-e "/rapidocr/d" \
+		${WRKSRC}/pyproject.toml
+	@${REINPLACE_CMD} -e 's|"python",|"${PYTHON_CMD}",|' \
+		${WRKSRC}/immich_ml/__main__.py
+
+.include <bsd.port.mk>
diff --git a/www/immich-ml/distinfo b/www/immich-ml/distinfo
new file mode 100644
index 000000000000..0a863ce782aa
--- /dev/null
+++ b/www/immich-ml/distinfo
@@ -0,0 +1,3 @@
+TIMESTAMP = 1786859142
+SHA256 (immich-app-immich-v3.1.0_GH0.tar.gz) = af0fba69cc5830093392de4f5576eeb7f2ccf28ba55154b7598e10f596fdfb40
+SIZE (immich-app-immich-v3.1.0_GH0.tar.gz) = 74961232
diff --git a/www/immich-ml/files/immich_ml.in b/www/immich-ml/files/immich_ml.in
new file mode 100644
index 000000000000..15db43dcd488
--- /dev/null
+++ b/www/immich-ml/files/immich_ml.in
@@ -0,0 +1,65 @@
+#!/bin/sh
+
+# PROVIDE: immich_ml
+# REQUIRE: LOGIN
+# KEYWORD: shutdown
+#
+# Add these lines to /etc/rc.conf.local or /etc/rc.conf
+# to enable this service:
+#
+# immich_ml_enable (bool):	Set to NO by default.
+#				Set it to YES to enable immich-ml.
+# immich_ml_cache_folder (str):	Model cache directory.
+#				Default: /var/db/immich-ml
+# immich_ml_host (str):		Listen address. Default: 127.0.0.1
+# immich_ml_port (int):		Listen port. Default: 3003
+
+. /etc/rc.subr
+
+name=immich_ml
+rcvar=immich_ml_enable
+
+load_rc_config $name
+
+: ${immich_ml_enable:=NO}
+: ${immich_ml_user:=immich}
+: ${immich_ml_group:=immich}
+: ${immich_ml_cache_folder:=/var/db/immich-ml}
+: ${immich_ml_host:=127.0.0.1}
+: ${immich_ml_port:=3003}
+: ${immich_ml_env:="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
+	HOME=${immich_ml_cache_folder} \
+	MACHINE_LEARNING_CACHE_FOLDER=${immich_ml_cache_folder} \
+	IMMICH_HOST=${immich_ml_host} \
+	IMMICH_PORT=${immich_ml_port}"}
+
+pidfile=/var/run/immich/${name}.pid
+
+start_precmd=immich_ml_precmd
+stop_cmd=immich_ml_stop
+command=/usr/sbin/daemon
+command_args="-f -S -P ${pidfile} \
+	-o /var/log/immich/${name}.log \
+	%%PYTHON_CMD%% -m immich_ml"
+
+immich_ml_stop()
+{
+	local pid
+	pid=$(check_pidfile ${pidfile} ${command})
+	if [ -z "$pid" ]; then
+		echo "${name} not running? (check ${pidfile})."
+		return 1
+	fi
+	echo "Stopping ${name}."
+	kill -TERM -- -${pid} 2>/dev/null || kill -TERM ${pid}
+	wait_for_pids ${pid}
+	rm -f ${pidfile}
+}
+
+immich_ml_precmd()
+{
+	install -d -o ${immich_ml_user} -g ${immich_ml_group} \
+		/var/run/immich /var/log/immich ${immich_ml_cache_folder}
+}
+
+run_rc_command "$1"
diff --git a/www/immich-ml/files/patch-immich__ml_models_____init____.py b/www/immich-ml/files/patch-immich__ml_models_____init____.py
new file mode 100644
index 000000000000..4194c4d22c4b
--- /dev/null
+++ b/www/immich-ml/files/patch-immich__ml_models_____init____.py
@@ -0,0 +1,31 @@
+--- immich_ml/models/__init__.py.orig	2026-08-16 16:21:56 UTC
++++ immich_ml/models/__init__.py
+@@ -3,8 +3,12 @@ from immich_ml.models.clip.visual import OpenClipVisua
+ 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.models.ocr.detection import TextDetector
+-from immich_ml.models.ocr.recognition import TextRecognizer
++try:
++    from immich_ml.models.ocr.detection import TextDetector
++    from immich_ml.models.ocr.recognition import TextRecognizer
++except ImportError:  # rapidocr has not been ported to FreeBSD yet
++    TextDetector = None  # type: ignore[assignment,misc]
++    TextRecognizer = None  # type: ignore[assignment,misc]
+ from immich_ml.schemas import ModelSource, ModelTask, ModelType
+ 
+ from .constants import get_model_source
+@@ -31,9 +35,13 @@ def get_model_class(model_name: str, model_type: Model
+             return FaceRecognizer
+ 
+         case ModelSource.PADDLE, ModelType.DETECTION, ModelTask.OCR:
++            if TextDetector is None:
++                raise ValueError("OCR models require rapidocr, which is not available")
+             return TextDetector
+ 
+         case ModelSource.PADDLE, ModelType.RECOGNITION, ModelTask.OCR:
++            if TextRecognizer is None:
++                raise ValueError("OCR models require rapidocr, which is not available")
+             return TextRecognizer
+ 
+         case _:
diff --git a/www/immich-ml/pkg-descr b/www/immich-ml/pkg-descr
new file mode 100644
index 000000000000..26fac63e8a1a
--- /dev/null
+++ b/www/immich-ml/pkg-descr
@@ -0,0 +1,10 @@
+The Immich machine-learning service provides smart search (CLIP),
+duplicate detection and facial recognition for an Immich server.
+It is a FastAPI application that executes ONNX models with
+onnxruntime on the CPU. Models are downloaded automatically on
+first use into the cache directory.
+
+The service listens on port 3003 by default and is consumed by
+www/immich; point the Immich server at it via its machine-learning
+URL setting. OCR (text search in images) is currently not
+available on FreeBSD because rapidocr is not ported yet.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.