AI: Add support for OLLAMA_BASE_URL env expansion in vision.yml #5361

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer
2025-12-10 10:52:26 +01:00
parent d5c56d4e7e
commit 75f183aa25
46 changed files with 226 additions and 102 deletions

View File

@@ -38,6 +38,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
GO111MODULE="on" \ GO111MODULE="on" \
CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \ CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \
PROG="photoprism" \ PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434" \
S6_KEEP_ENV=1 \ S6_KEEP_ENV=1 \
S6_VERBOSITY=0 \ S6_VERBOSITY=0 \
S6_LOGGING=0 S6_LOGGING=0

View File

@@ -32,7 +32,8 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
TF_CPP_MIN_LOG_LEVEL=4 \ TF_CPP_MIN_LOG_LEVEL=4 \
TF_ENABLE_ONEDNN_OPTS=1 \ TF_ENABLE_ONEDNN_OPTS=1 \
MALLOC_ARENA_MAX=2 \ MALLOC_ARENA_MAX=2 \
PROG="photoprism" PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434"
# Copy scripts and package sources config. # Copy scripts and package sources config.
COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/ COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/

View File

@@ -37,7 +37,8 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
GOBIN="/usr/local/bin" \ GOBIN="/usr/local/bin" \
GO111MODULE="on" \ GO111MODULE="on" \
CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \ CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \
PROG="photoprism" PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434"
# Copy scripts and package sources config. # Copy scripts and package sources config.
COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/ COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/

View File

@@ -32,7 +32,8 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
TF_CPP_MIN_LOG_LEVEL=4 \ TF_CPP_MIN_LOG_LEVEL=4 \
TF_ENABLE_ONEDNN_OPTS=1 \ TF_ENABLE_ONEDNN_OPTS=1 \
MALLOC_ARENA_MAX=2 \ MALLOC_ARENA_MAX=2 \
PROG="photoprism" PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434"
# copy scripts and debian backports sources list # copy scripts and debian backports sources list
COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/ COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/

View File

@@ -37,7 +37,8 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
GOBIN="/usr/local/bin" \ GOBIN="/usr/local/bin" \
GO111MODULE="on" \ GO111MODULE="on" \
CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \ CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \
PROG="photoprism" PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434"
# copy scripts and debian backports sources list # copy scripts and debian backports sources list
COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/ COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/

View File

@@ -37,7 +37,8 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
GOBIN="/usr/local/bin" \ GOBIN="/usr/local/bin" \
GO111MODULE="on" \ GO111MODULE="on" \
CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \ CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \
PROG="photoprism" PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434"
# copy scripts and debian backports sources list # copy scripts and debian backports sources list
COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/ COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/

View File

@@ -37,7 +37,8 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
GOBIN="/usr/local/bin" \ GOBIN="/usr/local/bin" \
GO111MODULE="on" \ GO111MODULE="on" \
CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \ CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \
PROG="photoprism" PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434"
# copy scripts and debian backports sources list # copy scripts and debian backports sources list
COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/ COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/

View File

@@ -33,6 +33,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
TF_ENABLE_ONEDNN_OPTS=1 \ TF_ENABLE_ONEDNN_OPTS=1 \
MALLOC_ARENA_MAX=2 \ MALLOC_ARENA_MAX=2 \
PROG="photoprism" \ PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434" \
S6_KEEP_ENV=0 \ S6_KEEP_ENV=0 \
S6_VERBOSITY=0 \ S6_VERBOSITY=0 \
S6_LOGGING=0 S6_LOGGING=0

View File

@@ -38,6 +38,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
GO111MODULE="on" \ GO111MODULE="on" \
CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \ CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \
PROG="photoprism" \ PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434" \
S6_KEEP_ENV=1 \ S6_KEEP_ENV=1 \
S6_VERBOSITY=0 \ S6_VERBOSITY=0 \
S6_LOGGING=0 S6_LOGGING=0

View File

@@ -32,7 +32,8 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
TF_CPP_MIN_LOG_LEVEL=4 \ TF_CPP_MIN_LOG_LEVEL=4 \
TF_ENABLE_ONEDNN_OPTS=1 \ TF_ENABLE_ONEDNN_OPTS=1 \
MALLOC_ARENA_MAX=2 \ MALLOC_ARENA_MAX=2 \
PROG="photoprism" PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434"
# Copy scripts and package sources config. # Copy scripts and package sources config.
COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/ COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/

View File

@@ -37,7 +37,8 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
GOBIN="/usr/local/bin" \ GOBIN="/usr/local/bin" \
GO111MODULE="on" \ GO111MODULE="on" \
CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \ CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \
PROG="photoprism" PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434"
# Copy scripts and package sources config. # Copy scripts and package sources config.
COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/ COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/

View File

@@ -32,7 +32,8 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
TF_CPP_MIN_LOG_LEVEL=4 \ TF_CPP_MIN_LOG_LEVEL=4 \
TF_ENABLE_ONEDNN_OPTS=1 \ TF_ENABLE_ONEDNN_OPTS=1 \
MALLOC_ARENA_MAX=2 \ MALLOC_ARENA_MAX=2 \
PROG="photoprism" PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434"
# Copy scripts and package sources config. # Copy scripts and package sources config.
COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/ COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/

View File

@@ -37,7 +37,8 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
GOBIN="/usr/local/bin" \ GOBIN="/usr/local/bin" \
GO111MODULE="on" \ GO111MODULE="on" \
CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \ CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \
PROG="photoprism" PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434"
# Copy scripts and package sources config. # Copy scripts and package sources config.
COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/ COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/

View File

@@ -32,7 +32,8 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
TF_CPP_MIN_LOG_LEVEL=4 \ TF_CPP_MIN_LOG_LEVEL=4 \
TF_ENABLE_ONEDNN_OPTS=1 \ TF_ENABLE_ONEDNN_OPTS=1 \
MALLOC_ARENA_MAX=2 \ MALLOC_ARENA_MAX=2 \
PROG="photoprism" PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434"
# Copy scripts and package sources config. # Copy scripts and package sources config.
COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/ COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/

View File

@@ -37,7 +37,8 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
GOBIN="/usr/local/bin" \ GOBIN="/usr/local/bin" \
GO111MODULE="on" \ GO111MODULE="on" \
CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \ CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \
PROG="photoprism" PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434"
# Copy scripts and package sources config. # Copy scripts and package sources config.
COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/ COPY --chown=root:root --chmod=755 /scripts/dist/ /scripts/

View File

@@ -33,6 +33,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
TF_ENABLE_ONEDNN_OPTS=1 \ TF_ENABLE_ONEDNN_OPTS=1 \
MALLOC_ARENA_MAX=2 \ MALLOC_ARENA_MAX=2 \
PROG="photoprism" \ PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434" \
S6_KEEP_ENV=0 \ S6_KEEP_ENV=0 \
S6_VERBOSITY=0 \ S6_VERBOSITY=0 \
S6_LOGGING=0 S6_LOGGING=0

View File

@@ -38,6 +38,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
GO111MODULE="on" \ GO111MODULE="on" \
CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \ CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \
PROG="photoprism" \ PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434" \
S6_KEEP_ENV=1 \ S6_KEEP_ENV=1 \
S6_VERBOSITY=0 \ S6_VERBOSITY=0 \
S6_LOGGING=0 S6_LOGGING=0

View File

@@ -33,6 +33,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
TF_ENABLE_ONEDNN_OPTS=1 \ TF_ENABLE_ONEDNN_OPTS=1 \
MALLOC_ARENA_MAX=2 \ MALLOC_ARENA_MAX=2 \
PROG="photoprism" \ PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434" \
S6_KEEP_ENV=0 \ S6_KEEP_ENV=0 \
S6_VERBOSITY=0 \ S6_VERBOSITY=0 \
S6_LOGGING=0 S6_LOGGING=0

View File

@@ -38,6 +38,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
GO111MODULE="on" \ GO111MODULE="on" \
CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \ CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \
PROG="photoprism" \ PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434" \
S6_KEEP_ENV=1 \ S6_KEEP_ENV=1 \
S6_VERBOSITY=0 \ S6_VERBOSITY=0 \
S6_LOGGING=0 S6_LOGGING=0

View File

@@ -33,6 +33,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
TF_ENABLE_ONEDNN_OPTS=1 \ TF_ENABLE_ONEDNN_OPTS=1 \
MALLOC_ARENA_MAX=2 \ MALLOC_ARENA_MAX=2 \
PROG="photoprism" \ PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434"
S6_KEEP_ENV=0 \ S6_KEEP_ENV=0 \
S6_VERBOSITY=0 \ S6_VERBOSITY=0 \
S6_LOGGING=0 S6_LOGGING=0

View File

@@ -38,6 +38,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
GO111MODULE="on" \ GO111MODULE="on" \
CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \ CGO_CFLAGS="-g -O2 -Wno-return-local-addr" \
PROG="photoprism" \ PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434"
S6_KEEP_ENV=1 \ S6_KEEP_ENV=1 \
S6_VERBOSITY=0 \ S6_VERBOSITY=0 \
S6_LOGGING=0 S6_LOGGING=0

View File

@@ -50,6 +50,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
TF_CPP_MIN_LOG_LEVEL=4 \ TF_CPP_MIN_LOG_LEVEL=4 \
MALLOC_ARENA_MAX=2 \ MALLOC_ARENA_MAX=2 \
PROG="photoprism" \ PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434"
PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \ PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \
PHOTOPRISM_IMPORT_PATH="/photoprism/import" \ PHOTOPRISM_IMPORT_PATH="/photoprism/import" \
PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \ PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \

View File

@@ -45,6 +45,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
TF_ENABLE_ONEDNN_OPTS=1 \ TF_ENABLE_ONEDNN_OPTS=1 \
MALLOC_ARENA_MAX=2 \ MALLOC_ARENA_MAX=2 \
PROG="photoprism" \ PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434" \
PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \ PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \
PHOTOPRISM_IMPORT_PATH="/photoprism/import" \ PHOTOPRISM_IMPORT_PATH="/photoprism/import" \
PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \ PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \

View File

@@ -45,6 +45,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
TF_ENABLE_ONEDNN_OPTS=1 \ TF_ENABLE_ONEDNN_OPTS=1 \
MALLOC_ARENA_MAX=2 \ MALLOC_ARENA_MAX=2 \
PROG="photoprism" \ PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434" \
PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \ PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \
PHOTOPRISM_IMPORT_PATH="/photoprism/import" \ PHOTOPRISM_IMPORT_PATH="/photoprism/import" \
PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \ PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \

View File

@@ -48,6 +48,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
TF_ENABLE_ONEDNN_OPTS=1 \ TF_ENABLE_ONEDNN_OPTS=1 \
MALLOC_ARENA_MAX=2 \ MALLOC_ARENA_MAX=2 \
PROG="photoprism" \ PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434" \
PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \ PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \
PHOTOPRISM_IMPORT_PATH="/photoprism/import" \ PHOTOPRISM_IMPORT_PATH="/photoprism/import" \
PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \ PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \

View File

@@ -48,6 +48,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
TF_ENABLE_ONEDNN_OPTS=1 \ TF_ENABLE_ONEDNN_OPTS=1 \
MALLOC_ARENA_MAX=2 \ MALLOC_ARENA_MAX=2 \
PROG="photoprism" \ PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434" \
PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \ PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \
PHOTOPRISM_IMPORT_PATH="/photoprism/import" \ PHOTOPRISM_IMPORT_PATH="/photoprism/import" \
PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \ PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \

View File

@@ -46,6 +46,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
TF_ENABLE_ONEDNN_OPTS=1 \ TF_ENABLE_ONEDNN_OPTS=1 \
MALLOC_ARENA_MAX=2 \ MALLOC_ARENA_MAX=2 \
PROG="photoprism" \ PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434" \
PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \ PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \
PHOTOPRISM_IMPORT_PATH="/photoprism/import" \ PHOTOPRISM_IMPORT_PATH="/photoprism/import" \
PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \ PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \

View File

@@ -46,6 +46,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
TF_ENABLE_ONEDNN_OPTS=1 \ TF_ENABLE_ONEDNN_OPTS=1 \
MALLOC_ARENA_MAX=2 \ MALLOC_ARENA_MAX=2 \
PROG="photoprism" \ PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434" \
PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \ PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \
PHOTOPRISM_IMPORT_PATH="/photoprism/import" \ PHOTOPRISM_IMPORT_PATH="/photoprism/import" \
PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \ PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \

View File

@@ -46,6 +46,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
TF_ENABLE_ONEDNN_OPTS=1 \ TF_ENABLE_ONEDNN_OPTS=1 \
MALLOC_ARENA_MAX=2 \ MALLOC_ARENA_MAX=2 \
PROG="photoprism" \ PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434" \
PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \ PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \
PHOTOPRISM_IMPORT_PATH="/photoprism/import" \ PHOTOPRISM_IMPORT_PATH="/photoprism/import" \
PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \ PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \

View File

@@ -46,6 +46,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
TF_ENABLE_ONEDNN_OPTS=1 \ TF_ENABLE_ONEDNN_OPTS=1 \
MALLOC_ARENA_MAX=2 \ MALLOC_ARENA_MAX=2 \
PROG="photoprism" \ PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434" \
PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \ PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \
PHOTOPRISM_IMPORT_PATH="/photoprism/import" \ PHOTOPRISM_IMPORT_PATH="/photoprism/import" \
PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \ PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \

View File

@@ -46,6 +46,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
TF_ENABLE_ONEDNN_OPTS=1 \ TF_ENABLE_ONEDNN_OPTS=1 \
MALLOC_ARENA_MAX=2 \ MALLOC_ARENA_MAX=2 \
PROG="photoprism" \ PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434" \
PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \ PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \
PHOTOPRISM_IMPORT_PATH="/photoprism/import" \ PHOTOPRISM_IMPORT_PATH="/photoprism/import" \
PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \ PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \

View File

@@ -46,6 +46,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
TF_ENABLE_ONEDNN_OPTS=1 \ TF_ENABLE_ONEDNN_OPTS=1 \
MALLOC_ARENA_MAX=2 \ MALLOC_ARENA_MAX=2 \
PROG="photoprism" \ PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434" \
PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \ PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \
PHOTOPRISM_IMPORT_PATH="/photoprism/import" \ PHOTOPRISM_IMPORT_PATH="/photoprism/import" \
PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \ PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \

View File

@@ -46,6 +46,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
TF_ENABLE_ONEDNN_OPTS=1 \ TF_ENABLE_ONEDNN_OPTS=1 \
MALLOC_ARENA_MAX=2 \ MALLOC_ARENA_MAX=2 \
PROG="photoprism" \ PROG="photoprism" \
OLLAMA_BASE_URL="http://ollama:11434" \
PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \ PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \
PHOTOPRISM_IMPORT_PATH="/photoprism/import" \ PHOTOPRISM_IMPORT_PATH="/photoprism/import" \
PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \ PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \

View File

@@ -1,13 +1,13 @@
## PhotoPrism — Vision Package ## PhotoPrism — Vision Package
**Last Updated:** December 2, 2025 **Last Updated:** December 10, 2025
### Overview ### Overview
`internal/ai/vision` provides the shared model registry, request builders, and parsers that power PhotoPrisms caption, label, face, NSFW, and future generate workflows. It reads `vision.yml`, normalizes models, and dispatches calls to one of three engines: `internal/ai/vision` provides the shared model registry, request builders, and parsers that power PhotoPrisms caption, label, face, NSFW, and future generate workflows. It reads `vision.yml`, normalizes models, and dispatches calls to one of three engines:
- **TensorFlow (builtin)** — default Nasnet / NSFW / Facenet models, no remote service required. - **TensorFlow (builtin)** — default Nasnet / NSFW / Facenet models, no remote service required.
- **Ollama** — local or proxied multimodal LLMs. See [`ollama/README.md`](ollama/README.md) for tuning and schema details. - **Ollama** — local or proxied multimodal LLMs. See [`ollama/README.md`](ollama/README.md) for tuning and schema details. The engine defaults to `${OLLAMA_BASE_URL:-http://ollama:11434}/api/generate`, trimming any trailing slash on the base URL; set `OLLAMA_BASE_URL=https://ollama.com` to opt into cloud defaults.
- **OpenAI** — cloud Responses API. See [`openai/README.md`](openai/README.md) for prompts, schema variants, and header requirements. - **OpenAI** — cloud Responses API. See [`openai/README.md`](openai/README.md) for prompts, schema variants, and header requirements.
### Configuration ### Configuration
@@ -94,8 +94,8 @@ The model `Options` adjust model parameters such as temperature, top-p, and sche
Configures the endpoint URL, method, format, and authentication for [Ollama](ollama/README.md), [OpenAI](openai/README.md), and other engines that perform remote HTTP requests: Configures the endpoint URL, method, format, and authentication for [Ollama](ollama/README.md), [OpenAI](openai/README.md), and other engines that perform remote HTTP requests:
| Field | Default | Notes | | Field | Default | Notes |
|------------------------------------|------------------------------------------|------------------------------------------------------------------------------------------| |------------------------------------|------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|
| `Uri` | required for remote | Endpoint base. Empty keeps model local (TensorFlow). | | `Uri` | required for remote | Endpoint base. Empty keeps model local (TensorFlow). Ollama alias fills `${OLLAMA_BASE_URL}/api/generate`, defaulting to `http://ollama:11434`. |
| `Method` | `POST` | Override verb if provider needs it. | | `Method` | `POST` | Override verb if provider needs it. |
| `Key` | `""` | Bearer token; prefer env expansion (OpenAI: `OPENAI_API_KEY`, Ollama: `OLLAMA_API_KEY`). | | `Key` | `""` | Bearer token; prefer env expansion (OpenAI: `OPENAI_API_KEY`, Ollama: `OLLAMA_API_KEY`). |
| `Username` / `Password` | `""` | Injected as basic auth when URI lacks userinfo. | | `Username` / `Password` | `""` | Injected as basic auth when URI lacks userinfo. |
@@ -142,7 +142,7 @@ Models:
Engine: ollama Engine: ollama
Run: newly-indexed Run: newly-indexed
Service: Service:
Uri: http://ollama:11434/api/generate Uri: ${OLLAMA_BASE_URL}/api/generate
``` ```
More Ollama guidance: [`internal/ai/vision/ollama/README.md`](ollama/README.md). More Ollama guidance: [`internal/ai/vision/ollama/README.md`](ollama/README.md).

View File

@@ -28,25 +28,25 @@ func init() {
} }
// registerOllamaEngineDefaults selects the default Ollama endpoint based on the // registerOllamaEngineDefaults selects the default Ollama endpoint based on the
// available credentials and registers the engine alias accordingly. When an // configured base URL and registers the engine alias accordingly. When
// API key is configured, we default to the hosted Cloud endpoint; otherwise we // OLLAMA_BASE_URL points at the cloud host we only switch the default model to
// assume a self-hosted instance reachable via the docker-compose default. // the cloud preset; the actual base URL continues to come from
// This keeps the zero-config path fast for local dev while automatically using // OLLAMA_BASE_URL (or falls back to the local compose default) so we don't
// the cloud service when credentials are present. // accidentally talk to the hosted service without an explicit endpoint.
func registerOllamaEngineDefaults() { func registerOllamaEngineDefaults() {
defaultModel := ollama.DefaultModel ensureEnv()
defaultUri := ollama.DefaultUri
// Detect Ollama cloud API key. defaultModel := ollama.DefaultModel
if key := os.Getenv(ollama.APIKeyEnv); len(key) > 50 && strings.Contains(key, ".") {
// Use different default model for the Ollama cloud service.
if baseUrl := os.Getenv(ollama.BaseUrlEnv); baseUrl == ollama.CloudBaseUrl {
defaultModel = ollama.CloudModel defaultModel = ollama.CloudModel
defaultUri = ollama.CloudUri
} }
// Register the human-friendly engine name so configuration can simply use // Register the human-friendly engine name so configuration can simply use
// `Engine: "ollama"` and inherit adapter defaults. // `Engine: "ollama"` and inherit adapter defaults.
RegisterEngineAlias(ollama.EngineName, EngineInfo{ RegisterEngineAlias(ollama.EngineName, EngineInfo{
Uri: defaultUri, Uri: ollama.DefaultUri,
RequestFormat: ApiFormatOllama, RequestFormat: ApiFormatOllama,
ResponseFormat: ApiFormatOllama, ResponseFormat: ApiFormatOllama,
FileScheme: scheme.Base64, FileScheme: scheme.Base64,

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"os" "os"
"sync"
"testing" "testing"
"github.com/photoprism/photoprism/internal/ai/vision/ollama" "github.com/photoprism/photoprism/internal/ai/vision/ollama"
@@ -29,6 +30,7 @@ func TestRegisterOllamaEngineDefaults(t *testing.T) {
}) })
t.Run("SelfHosted", func(t *testing.T) { t.Run("SelfHosted", func(t *testing.T) {
ensureEnvOnce = sync.Once{}
CaptionModel = testCaptionModel.Clone() CaptionModel = testCaptionModel.Clone()
_ = os.Unsetenv(ollama.APIKeyEnv) _ = os.Unsetenv(ollama.APIKeyEnv)
@@ -56,8 +58,9 @@ func TestRegisterOllamaEngineDefaults(t *testing.T) {
} }
}) })
t.Run("Cloud", func(t *testing.T) { t.Run("Cloud", func(t *testing.T) {
ensureEnvOnce = sync.Once{}
CaptionModel = testCaptionModel.Clone() CaptionModel = testCaptionModel.Clone()
t.Setenv(ollama.APIKeyEnv, cloudToken) t.Setenv(ollama.BaseUrlEnv, ollama.CloudBaseUrl+"/")
registerOllamaEngineDefaults() registerOllamaEngineDefaults()
@@ -66,8 +69,8 @@ func TestRegisterOllamaEngineDefaults(t *testing.T) {
t.Fatalf("expected engine info for %s", ollama.EngineName) t.Fatalf("expected engine info for %s", ollama.EngineName)
} }
if info.Uri != ollama.CloudUri { if info.Uri != ollama.DefaultUri {
t.Fatalf("expected cloud uri %s, got %s", ollama.CloudUri, info.Uri) t.Fatalf("expected default uri %s, got %s", ollama.DefaultUri, info.Uri)
} }
if info.DefaultModel != ollama.CloudModel { if info.DefaultModel != ollama.CloudModel {
@@ -78,14 +81,31 @@ func TestRegisterOllamaEngineDefaults(t *testing.T) {
t.Fatalf("expected caption model %s, got %s", ollama.CloudModel, CaptionModel.Model) t.Fatalf("expected caption model %s, got %s", ollama.CloudModel, CaptionModel.Model)
} }
if CaptionModel.Service.Uri != ollama.CloudUri { if CaptionModel.Service.Uri != ollama.DefaultUri {
t.Fatalf("expected caption model uri %s, got %s", ollama.CloudUri, CaptionModel.Service.Uri) t.Fatalf("expected caption model uri %s, got %s", ollama.DefaultUri, CaptionModel.Service.Uri)
}
})
t.Run("ApiKeyAloneKeepsLocalDefaults", func(t *testing.T) {
ensureEnvOnce = sync.Once{}
CaptionModel = testCaptionModel.Clone()
t.Setenv(ollama.APIKeyEnv, cloudToken)
registerOllamaEngineDefaults()
info, ok := EngineInfoFor(ollama.EngineName)
if !ok {
t.Fatalf("expected engine info for %s", ollama.EngineName)
}
if info.DefaultModel != ollama.DefaultModel {
t.Fatalf("expected default model %s, got %s", ollama.DefaultModel, info.DefaultModel)
} }
}) })
t.Run("NewModels", func(t *testing.T) { t.Run("NewModels", func(t *testing.T) {
ensureEnvOnce = sync.Once{}
CaptionModel = testCaptionModel.Clone() CaptionModel = testCaptionModel.Clone()
t.Setenv(ollama.APIKeyEnv, cloudToken) t.Setenv(ollama.BaseUrlEnv, ollama.CloudBaseUrl)
registerOllamaEngineDefaults() registerOllamaEngineDefaults()
model := &Model{Type: ModelTypeCaption, Engine: ollama.EngineName} model := &Model{Type: ModelTypeCaption, Engine: ollama.EngineName}
@@ -95,8 +115,8 @@ func TestRegisterOllamaEngineDefaults(t *testing.T) {
t.Fatalf("expected model %s, got %s", ollama.CloudModel, model.Model) t.Fatalf("expected model %s, got %s", ollama.CloudModel, model.Model)
} }
if model.Service.Uri != ollama.CloudUri { if model.Service.Uri != ollama.DefaultUri {
t.Fatalf("expected service uri %s, got %s", ollama.CloudUri, model.Service.Uri) t.Fatalf("expected service uri %s, got %s", ollama.DefaultUri, model.Service.Uri)
} }
if model.Service.RequestFormat != ApiFormatOllama || model.Service.ResponseFormat != ApiFormatOllama { if model.Service.RequestFormat != ApiFormatOllama || model.Service.ResponseFormat != ApiFormatOllama {

View File

@@ -1,14 +1,14 @@
## PhotoPrism — Ollama Engine Integration ## PhotoPrism — Ollama Engine Integration
**Last Updated:** November 14, 2025 **Last Updated:** December 10, 2025
### Overview ### Overview
This package provides PhotoPrisms native adapter for Ollama-compatible multimodal models. It lets Caption, Labels, and future Generate workflows call locally hosted models without changing worker logic, reusing the shared API client (`internal/ai/vision/api_client.go`) and result types (`LabelResult`, `CaptionResult`). Requests stay inside your infrastructure, rely on base64 thumbnails, and honor the same ACL, timeout, and logging hooks as the default TensorFlow engines. This package provides PhotoPrisms native adapter for Ollama-compatible multimodal models. It lets Caption, Labels, and future Generate workflows call locally hosted models without changing worker logic, reusing the shared API client (`internal/ai/vision/api_client.go`) and result types (`LabelResult`, `CaptionResult`). Requests stay inside your infrastructure, rely on base64 thumbnails, and honor the same ACL, timeout, and logging hooks as the default TensorFlow engines. The adapter resolves `${OLLAMA_BASE_URL}/api/generate`, trimming trailing slashes and defaulting to `http://ollama:11434`; set `OLLAMA_BASE_URL=https://ollama.com` to opt into cloud defaults.
#### Context & Constraints #### Context & Constraints
- Engine defaults live in `internal/ai/vision/ollama` and are applied whenever a model sets `Engine: ollama`. Aliases map to `ApiFormatOllama`, `scheme.Base64`, and a default 720px thumbnail. - Engine defaults live in `internal/ai/vision/ollama` and are applied whenever a model sets `Engine: ollama`. Aliases map to `ApiFormatOllama`, `scheme.Base64`, and a default 720px thumbnail. Cloud defaults are only selected when `OLLAMA_BASE_URL` equals `https://ollama.com`.
- Responses may arrive as newline-delimited JSON chunks. `decodeOllamaResponse` keeps the most recent chunk, while `parseOllamaLabels` replays plain JSON strings found in `response`. - Responses may arrive as newline-delimited JSON chunks. `decodeOllamaResponse` keeps the most recent chunk, while `parseOllamaLabels` replays plain JSON strings found in `response`.
- Structured JSON is optional for captions but enforced for labels when `Format: json` (default for label models targeting the Ollama engine). - Structured JSON is optional for captions but enforced for labels when `Format: json` (default for label models targeting the Ollama engine).
- The adapter never overwrites TensorFlow defaults. If an Ollama call fails, downstream code still has Nasnet, NSFW, and Face models available. - The adapter never overwrites TensorFlow defaults. If an Ollama call fails, downstream code still has Nasnet, NSFW, and Face models available.
@@ -73,6 +73,7 @@ This package provides PhotoPrisms native adapter for Ollama-compatible multim
- `PHOTOPRISM_VISION_YAML` — Custom `vision.yml` path. Keep it synced in Git if you automate deployments. - `PHOTOPRISM_VISION_YAML` — Custom `vision.yml` path. Keep it synced in Git if you automate deployments.
- `OLLAMA_HOST`, `OLLAMA_MODELS`, `OLLAMA_MAX_QUEUE`, `OLLAMA_NUM_PARALLEL`, etc. — Provided in `compose*.yaml` to tune the Ollama daemon. Adjust `OLLAMA_KEEP_ALIVE` if you want models to stay loaded between worker batches. - `OLLAMA_HOST`, `OLLAMA_MODELS`, `OLLAMA_MAX_QUEUE`, `OLLAMA_NUM_PARALLEL`, etc. — Provided in `compose*.yaml` to tune the Ollama daemon. Adjust `OLLAMA_KEEP_ALIVE` if you want models to stay loaded between worker batches.
- `OLLAMA_API_KEY` / `OLLAMA_API_KEY_FILE` — Default bearer token picked up when `Service.Key` is empty; useful for hosted Ollama services (e.g., Ollama Cloud). - `OLLAMA_API_KEY` / `OLLAMA_API_KEY_FILE` — Default bearer token picked up when `Service.Key` is empty; useful for hosted Ollama services (e.g., Ollama Cloud).
- `OLLAMA_BASE_URL` — Base URL for the Ollama API; defaults to `http://ollama:11434`, trailing slashes are trimmed. Set to `https://ollama.com` to enable cloud defaults.
- `PHOTOPRISM_LOG_LEVEL=trace` — Enables verbose request/response previews (truncated to avoid leaking images). Use temporarily when debugging parsing issues. - `PHOTOPRISM_LOG_LEVEL=trace` — Enables verbose request/response previews (truncated to avoid leaking images). Use temporarily when debugging parsing issues.
#### `vision.yml` Example #### `vision.yml` Example
@@ -90,7 +91,7 @@ Models:
Stop: ["\n\n"] Stop: ["\n\n"]
ForceJson: true ForceJson: true
Service: Service:
Uri: http://ollama:11434/api/generate Uri: ${OLLAMA_BASE_URL}/api/generate
RequestFormat: ollama RequestFormat: ollama
ResponseFormat: ollama ResponseFormat: ollama
FileScheme: base64 FileScheme: base64
@@ -102,7 +103,7 @@ Models:
Options: Options:
Temperature: 0.2 Temperature: 0.2
Service: Service:
Uri: http://ollama:11434/api/generate Uri: ${OLLAMA_BASE_URL}/api/generate
``` ```
Guidelines: Guidelines:

View File

@@ -11,10 +11,16 @@ const (
APIKeyFileEnv = "OLLAMA_API_KEY_FILE" //nolint:gosec // environment variable name, not a secret APIKeyFileEnv = "OLLAMA_API_KEY_FILE" //nolint:gosec // environment variable name, not a secret
// APIKeyPlaceholder is the `${VAR}` form injected when no explicit key is provided. // APIKeyPlaceholder is the `${VAR}` form injected when no explicit key is provided.
APIKeyPlaceholder = "${" + APIKeyEnv + "}" APIKeyPlaceholder = "${" + APIKeyEnv + "}"
// BaseUrlEnv defines the environment variable used for the Ollama base URL e.g. "https://ollama.com" or "http://ollama:11434".
BaseUrlEnv = "OLLAMA_BASE_URL" //nolint:gosec // environment variable name, not a secret
// BaseUrlPlaceholder is the `${VAR}` form injected when no explicit URL is provided.
BaseUrlPlaceholder = "${" + BaseUrlEnv + "}"
// DefaultBaseUrl is the local Ollama endpoint used when the environment variable is unset.
DefaultBaseUrl = "http://ollama:11434"
// CloudBaseUrl is the base URL for the Ollama Cloud service.
CloudBaseUrl = "https://ollama.com"
// DefaultUri is the default service URI for self-hosted Ollama instances. // DefaultUri is the default service URI for self-hosted Ollama instances.
DefaultUri = "http://ollama:11434/api/generate" DefaultUri = BaseUrlPlaceholder + "/api/generate"
// CloudUri is the Ollama cloud service URI
CloudUri = "https://ollama.com/api/generate"
// DefaultModel names the default caption model bundled with our adapter defaults. // DefaultModel names the default caption model bundled with our adapter defaults.
DefaultModel = "gemma3:latest" DefaultModel = "gemma3:latest"
// CloudModel names the default caption for the Ollama cloud service, see https://ollama.com/cloud. // CloudModel names the default caption for the Ollama cloud service, see https://ollama.com/cloud.

View File

@@ -31,14 +31,18 @@ func (m *Service) Endpoint() (uri, method string) {
return "", "" return "", ""
} }
ensureEnv()
if uri = strings.TrimSpace(os.ExpandEnv(m.Uri)); strings.Contains(uri, "${") {
uri = ""
}
if m.Method != "" { if m.Method != "" {
method = m.Method method = m.Method
} else { } else {
method = ServiceMethod method = ServiceMethod
} }
uri = strings.TrimSpace(m.Uri)
if username, password := m.BasicAuth(); username != "" || password != "" { if username, password := m.BasicAuth(); username != "" || password != "" {
if parsed, err := url.Parse(uri); err == nil { if parsed, err := url.Parse(uri); err == nil {
if parsed.User == nil { if parsed.User == nil {

View File

@@ -33,10 +33,26 @@ func TestServiceEndpoint(t *testing.T) {
wantURI: "https://keep:me@vision.example.com", wantURI: "https://keep:me@vision.example.com",
wantMethod: ServiceMethod, wantMethod: ServiceMethod,
}, },
{
name: "ExpandsBaseUrlEnv",
svc: Service{Uri: "${OLLAMA_BASE_URL}/api/generate"},
wantURI: "http://custom:11434/api/generate",
wantMethod: ServiceMethod,
},
{
name: "FallbacksWhenEnvMissing",
svc: Service{Uri: "${OLLAMA_BASE_URL}/api/generate"},
wantURI: "http://ollama:11434/api/generate",
wantMethod: ServiceMethod,
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
if tt.name == "ExpandsBaseUrlEnv" {
t.Setenv("OLLAMA_BASE_URL", "http://custom:11434")
}
uri, method := tt.svc.Endpoint() uri, method := tt.svc.Endpoint()
if uri != tt.wantURI { if uri != tt.wantURI {
t.Fatalf("uri: got %q want %q", uri, tt.wantURI) t.Fatalf("uri: got %q want %q", uri, tt.wantURI)

View File

@@ -70,7 +70,7 @@ Models:
Run: manual Run: manual
Resolution: 720 Resolution: 720
Service: Service:
Uri: http://ollama:11434/api/generate Uri: ${OLLAMA_BASE_URL}/api/generate
Key: ${OLLAMA_API_KEY} Key: ${OLLAMA_API_KEY}
FileScheme: base64 FileScheme: base64
RequestFormat: ollama RequestFormat: ollama

View File

@@ -25,49 +25,7 @@ Additional information can be found in our Developer Guide:
package vision package vision
import ( import (
"os"
"strings"
"sync"
"github.com/photoprism/photoprism/internal/ai/vision/ollama"
"github.com/photoprism/photoprism/internal/ai/vision/openai"
"github.com/photoprism/photoprism/internal/event" "github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/pkg/clean"
"github.com/photoprism/photoprism/pkg/fs"
) )
var log = event.Log var log = event.Log
var ensureEnvOnce sync.Once
// ensureEnv loads environment-backed credentials once so adapters can look up
// OPENAI_API_KEY / OLLAMA_API_KEY even when operators rely on *_FILE fallbacks.
// Future engine integrations can reuse this hook to normalise additional
// secrets.
func ensureEnv() {
ensureEnvOnce.Do(func() {
loadEnvKeyFromFile(openai.APIKeyEnv, openai.APIKeyFileEnv)
loadEnvKeyFromFile(ollama.APIKeyEnv, ollama.APIKeyFileEnv)
})
}
// loadEnvKeyFromFile populates envVar from fileVar when the environment value
// is empty and the referenced file exists and is non-empty.
func loadEnvKeyFromFile(envVar, fileVar string) {
if os.Getenv(envVar) != "" {
return
}
filePath := strings.TrimSpace(os.Getenv(fileVar))
if !fs.FileExistsNotEmpty(filePath) {
return
}
// #nosec G304 path provided via env
if data, err := os.ReadFile(filePath); err == nil {
if key := clean.Auth(string(data)); key != "" {
_ = os.Setenv(envVar, key)
}
}
}

View File

@@ -0,0 +1,61 @@
package vision
import (
"os"
"strings"
"sync"
"github.com/photoprism/photoprism/internal/ai/vision/ollama"
"github.com/photoprism/photoprism/internal/ai/vision/openai"
"github.com/photoprism/photoprism/pkg/clean"
"github.com/photoprism/photoprism/pkg/fs"
)
var ensureEnvOnce sync.Once
// ensureEnv loads environment-backed credentials once so adapters can look up
// OPENAI_API_KEY / OLLAMA_API_KEY even when operators rely on *_FILE fallbacks.
// Future engine integrations can reuse this hook to normalize additional
// secrets.
func ensureEnv() {
ensureEnvOnce.Do(func() {
loadEnvKeyFromFile(openai.APIKeyEnv, openai.APIKeyFileEnv)
loadEnvKeyFromFile(ollama.APIKeyEnv, ollama.APIKeyFileEnv)
// Init the Ollama base URL by trimming trailing slashes or using the default.
initEnvUrl(ollama.BaseUrlEnv, ollama.DefaultBaseUrl)
})
}
// initEnvUrl ensures that the variable contains no trailing
// slashes and sets a default value if it is missing.
func initEnvUrl(envName, defaultUrl string) {
if base := strings.TrimSpace(os.Getenv(envName)); base != "" {
if normalized := strings.TrimRight(base, "/"); normalized != base {
_ = os.Setenv(envName, normalized)
}
} else if defaultUrl != "" {
_ = os.Setenv(envName, defaultUrl)
}
}
// loadEnvKeyFromFile populates envVar from fileVar when the environment value
// is empty and the referenced file exists and is non-empty.
func loadEnvKeyFromFile(envVar, fileVar string) {
if os.Getenv(envVar) != "" {
return
}
filePath := strings.TrimSpace(os.Getenv(fileVar))
if !fs.FileExistsNotEmpty(filePath) {
return
}
// #nosec G304 path provided via env
if data, err := os.ReadFile(filePath); err == nil {
if key := clean.Auth(string(data)); key != "" {
_ = os.Setenv(envVar, key)
}
}
}

View File

@@ -6,6 +6,31 @@ import (
"testing" "testing"
) )
func TestInitEnvUrl(t *testing.T) {
const envName = "TEST_OLLAMA_BASE_URL"
// Case: trims trailing slash.
t.Setenv(envName, "http://example.com/")
initEnvUrl(envName, "")
if got := os.Getenv(envName); got != "http://example.com" {
t.Fatalf("trim: expected http://example.com, got %s", got)
}
// Case: sets default when unset.
t.Setenv(envName, "")
initEnvUrl(envName, "http://default.local")
if got := os.Getenv(envName); got != "http://default.local" {
t.Fatalf("default: expected http://default.local, got %s", got)
}
// Case: leaves already-normalized value untouched.
t.Setenv(envName, "http://kept.local")
initEnvUrl(envName, "http://ignored.local")
if got := os.Getenv(envName); got != "http://kept.local" {
t.Fatalf("preserve: expected http://kept.local, got %s", got)
}
}
// TestLoadEnvKeyFromFile verifies that loadEnvKeyFromFile reads API keys from // TestLoadEnvKeyFromFile verifies that loadEnvKeyFromFile reads API keys from
// *_FILE variables when the primary env var is empty. // *_FILE variables when the primary env var is empty.
func TestLoadEnvKeyFromFile(t *testing.T) { func TestLoadEnvKeyFromFile(t *testing.T) {