From 2390b4c921b64c905c4e866eeb8723b873c25df3 Mon Sep 17 00:00:00 2001 From: Andreas Klos Date: Mon, 16 Feb 2026 14:34:25 +0100 Subject: [PATCH 1/2] feat(vision): add rollout ADR and default-off flags --- ADR-vision-embedding-rollout.md | 53 +++++++++++++++++++ .../_admin_backend_and_extractor_helpers.tpl | 8 +++ .../rag/templates/_backend_helpers.tpl | 4 ++ .../templates/admin-backend/configmap.yaml | 9 ++++ .../templates/admin-backend/deployment.yaml | 2 + .../rag/templates/backend/configmap.yaml | 9 ++++ .../rag/templates/backend/deployment.yaml | 2 + .../rag/templates/extractor/configmap.yaml | 9 ++++ .../rag/templates/extractor/deployment.yaml | 2 + infrastructure/rag/values.yaml | 12 +++++ 10 files changed, 110 insertions(+) create mode 100644 ADR-vision-embedding-rollout.md diff --git a/ADR-vision-embedding-rollout.md b/ADR-vision-embedding-rollout.md new file mode 100644 index 00000000..5ea223f6 --- /dev/null +++ b/ADR-vision-embedding-rollout.md @@ -0,0 +1,53 @@ +# ADR: Vision Embedding Rollout (Flag-Gated) + +- Status: Accepted +- Date: 2026-02-16 + +## Context + +STACKIT published a vision-capable embedder. We want to integrate image-native retrieval into the existing RAG pipeline without destabilizing production traffic. + +The current platform already supports `IMAGE` as a content type, but image ingestion/retrieval behavior is not yet consistently routed through a dedicated vision lane end-to-end. + +## Decision + +Roll out vision support behind explicit feature flags that default to `false`: + +- `VISION_IMAGE_LANE_ENABLED=false` +- `VISION_EMBEDDING_ENABLED=false` +- `VISION_IMAGE_RETRIEVER_ENABLED=false` + +Flags are exposed via Helm values and config maps for backend, admin-backend, and extractor. + +## Consequences + +### Positive + +- No behavior change on merge while flags stay off. +- Safe staged rollout with reversible steps. +- Easier incident response by disabling a specific lane. + +### Negative + +- Temporary configuration overhead while both legacy and vision paths exist. +- Additional test matrix during rollout. + +## Rollout Notes + +- Keep all three flags `false` until the final rollout PR is merged and staging is validated. +- Enable in sequence on staging: + 1. `VISION_IMAGE_LANE_ENABLED` + 2. `VISION_EMBEDDING_ENABLED` + 3. `VISION_IMAGE_RETRIEVER_ENABLED` +- Promote to production only after mixed-modality retrieval checks pass. + +## Telemetry Baseline (No Behavior Change in This ADR) + +Track these counters from the first behavior PR onward: + +- `vision.image_documents_ingested_total` +- `vision.image_embeddings_written_total` +- `vision.image_retrieval_hits_total` +- `vision.image_retrieval_errors_total` + +This ADR only defines the rollout contract; metric instrumentation is introduced in later PRs. diff --git a/infrastructure/rag/templates/_admin_backend_and_extractor_helpers.tpl b/infrastructure/rag/templates/_admin_backend_and_extractor_helpers.tpl index cd35eba8..5ca3b9cd 100644 --- a/infrastructure/rag/templates/_admin_backend_and_extractor_helpers.tpl +++ b/infrastructure/rag/templates/_admin_backend_and_extractor_helpers.tpl @@ -140,10 +140,18 @@ {{- printf "%s-source-uploader-configmap" .Release.Name | trunc 63 | trimSuffix "-" -}} {{- end -}} +{{- define "configmap.adminVisionName" -}} +{{- printf "%s-admin-vision-configmap" .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + {{- define "configmap.extractorSitemapName" -}} {{- printf "%s-extractor-sitemap-configmap" .Release.Name | trunc 63 | trimSuffix "-" -}} {{- end -}} +{{- define "configmap.extractorVisionName" -}} +{{- printf "%s-extractor-vision-configmap" .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + # image {{- define "adminBackend.fullImageName" -}} {{- $tag := default .Chart.AppVersion .Values.adminBackend.image.tag -}} diff --git a/infrastructure/rag/templates/_backend_helpers.tpl b/infrastructure/rag/templates/_backend_helpers.tpl index b186f22b..1b72511b 100644 --- a/infrastructure/rag/templates/_backend_helpers.tpl +++ b/infrastructure/rag/templates/_backend_helpers.tpl @@ -103,6 +103,10 @@ basic-auth {{- printf "%s-retriever-configmap" .Release.Name | trunc 63 | trimSuffix "-" -}} {{- end -}} +{{- define "configmap.backendVisionName" -}} +{{- printf "%s-backend-vision-configmap" .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + {{- define "configmap.langfuseName" -}} {{- printf "%s-langfuse-configmap" .Release.Name | trunc 63 | trimSuffix "-" -}} {{- end -}} diff --git a/infrastructure/rag/templates/admin-backend/configmap.yaml b/infrastructure/rag/templates/admin-backend/configmap.yaml index af158c75..e8dfc8ec 100644 --- a/infrastructure/rag/templates/admin-backend/configmap.yaml +++ b/infrastructure/rag/templates/admin-backend/configmap.yaml @@ -42,3 +42,12 @@ data: {{- range $key, $value := .Values.adminBackend.envs.sourceUploader }} {{ $key }}: {{ $value | quote }} {{- end }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "configmap.adminVisionName" . }} +data: + {{- range $key, $value := .Values.adminBackend.envs.vision }} + {{ $key }}: {{ $value | quote }} + {{- end }} diff --git a/infrastructure/rag/templates/admin-backend/deployment.yaml b/infrastructure/rag/templates/admin-backend/deployment.yaml index e0694d18..73954274 100644 --- a/infrastructure/rag/templates/admin-backend/deployment.yaml +++ b/infrastructure/rag/templates/admin-backend/deployment.yaml @@ -117,6 +117,8 @@ spec: name: {{ template "configmap.keyValueStoreName" . }} - configMapRef: name: {{ template "configmap.sourceUploaderName" . }} + - configMapRef: + name: {{ template "configmap.adminVisionName" . }} - configMapRef: name: {{ template "configmap.retryDecoratorName" . }} - configMapRef: diff --git a/infrastructure/rag/templates/backend/configmap.yaml b/infrastructure/rag/templates/backend/configmap.yaml index c5621c64..7dd83640 100644 --- a/infrastructure/rag/templates/backend/configmap.yaml +++ b/infrastructure/rag/templates/backend/configmap.yaml @@ -18,6 +18,15 @@ data: --- apiVersion: v1 kind: ConfigMap +metadata: + name: {{ template "configmap.backendVisionName" . }} +data: + {{- range $key, $value := .Values.backend.envs.vision }} + {{ $key }}: {{ $value | quote }} + {{- end }} +--- +apiVersion: v1 +kind: ConfigMap metadata: name: {{ template "configmap.langfuseName" . }} data: diff --git a/infrastructure/rag/templates/backend/deployment.yaml b/infrastructure/rag/templates/backend/deployment.yaml index 5a89b19a..a5e75ea1 100644 --- a/infrastructure/rag/templates/backend/deployment.yaml +++ b/infrastructure/rag/templates/backend/deployment.yaml @@ -109,6 +109,8 @@ spec: name: {{ template "configmap.embedderClassTypesName" . }} - configMapRef: name: {{ template "configmap.retrieverName" . }} + - configMapRef: + name: {{ template "configmap.backendVisionName" . }} - configMapRef: name: {{ template "configmap.rerankerName" . }} - configMapRef: diff --git a/infrastructure/rag/templates/extractor/configmap.yaml b/infrastructure/rag/templates/extractor/configmap.yaml index 5f02f2c0..3f1819b9 100644 --- a/infrastructure/rag/templates/extractor/configmap.yaml +++ b/infrastructure/rag/templates/extractor/configmap.yaml @@ -6,3 +6,12 @@ data: {{- range $key, $value := .Values.extractor.envs.sitemap }} {{ $key }}: {{ $value | quote }} {{- end }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "configmap.extractorVisionName" . }} +data: + {{- range $key, $value := .Values.extractor.envs.vision }} + {{ $key }}: {{ $value | quote }} + {{- end }} diff --git a/infrastructure/rag/templates/extractor/deployment.yaml b/infrastructure/rag/templates/extractor/deployment.yaml index d51aaf88..26618aee 100644 --- a/infrastructure/rag/templates/extractor/deployment.yaml +++ b/infrastructure/rag/templates/extractor/deployment.yaml @@ -112,6 +112,8 @@ spec: name: {{ template "configmap.s3Name" . }} - configMapRef: name: {{ template "configmap.extractorSitemapName" . }} + - configMapRef: + name: {{ template "configmap.extractorVisionName" . }} - secretRef: name: {{ template "secret.s3RefName" . }} {{- $hfCacheDir := include "extractor.huggingfaceCacheDir" . }} diff --git a/infrastructure/rag/values.yaml b/infrastructure/rag/values.yaml index dd452881..78b56336 100644 --- a/infrastructure/rag/values.yaml +++ b/infrastructure/rag/values.yaml @@ -196,6 +196,10 @@ backend: RETRIEVER_TABLE_K_DOCUMENTS: 10 RETRIEVER_IMAGE_THRESHOLD: 0.7 RETRIEVER_IMAGE_K_DOCUMENTS: 10 + vision: + VISION_IMAGE_LANE_ENABLED: false + VISION_EMBEDDING_ENABLED: false + VISION_IMAGE_RETRIEVER_ENABLED: false errorMessages: ERROR_MESSAGES_NO_DOCUMENTS_MESSAGE: "I'm sorry, my responses are limited. You must ask the right questions." ERROR_MESSAGES_NO_OR_EMPTY_COLLECTION: "No documents were provided for searching." @@ -390,6 +394,10 @@ adminBackend: sourceUploader: # Large sitemap ingestions (per-page summaries) can take > 1 hour. SOURCE_UPLOADER_TIMEOUT: 3600 + vision: + VISION_IMAGE_LANE_ENABLED: false + VISION_EMBEDDING_ENABLED: false + VISION_IMAGE_RETRIEVER_ENABLED: false extractor: replicaCount: 1 @@ -448,6 +456,10 @@ extractor: # Options: "docusaurus" (default), "astro", "generic" # Note: https://docs.stackit.cloud is built with Astro/Starlight -> use "astro". SITEMAP_PARSER: docusaurus + vision: + VISION_IMAGE_LANE_ENABLED: false + VISION_EMBEDDING_ENABLED: false + VISION_IMAGE_RETRIEVER_ENABLED: false adminFrontend: name: admin-frontend From 14757b153829f57df03b24507ce7c0e1e463b22d Mon Sep 17 00:00:00 2001 From: Andreas Klos Date: Mon, 16 Feb 2026 15:11:45 +0100 Subject: [PATCH 2/2] refactor(vision): use one shared flags configmap --- ADR-vision-embedding-rollout.md | 2 +- .../_admin_backend_and_extractor_helpers.tpl | 8 -------- .../rag/templates/_backend_helpers.tpl | 4 ++-- .../rag/templates/admin-backend/configmap.yaml | 9 --------- .../rag/templates/admin-backend/deployment.yaml | 2 +- .../rag/templates/backend/configmap.yaml | 4 ++-- .../rag/templates/backend/deployment.yaml | 2 +- .../rag/templates/extractor/configmap.yaml | 9 --------- .../rag/templates/extractor/deployment.yaml | 2 +- infrastructure/rag/values.yaml | 16 ++++------------ 10 files changed, 12 insertions(+), 46 deletions(-) diff --git a/ADR-vision-embedding-rollout.md b/ADR-vision-embedding-rollout.md index 5ea223f6..2f102af7 100644 --- a/ADR-vision-embedding-rollout.md +++ b/ADR-vision-embedding-rollout.md @@ -17,7 +17,7 @@ Roll out vision support behind explicit feature flags that default to `false`: - `VISION_EMBEDDING_ENABLED=false` - `VISION_IMAGE_RETRIEVER_ENABLED=false` -Flags are exposed via Helm values and config maps for backend, admin-backend, and extractor. +Flags are exposed via Helm values and a shared config map consumed by backend, admin-backend, and extractor. ## Consequences diff --git a/infrastructure/rag/templates/_admin_backend_and_extractor_helpers.tpl b/infrastructure/rag/templates/_admin_backend_and_extractor_helpers.tpl index 5ca3b9cd..cd35eba8 100644 --- a/infrastructure/rag/templates/_admin_backend_and_extractor_helpers.tpl +++ b/infrastructure/rag/templates/_admin_backend_and_extractor_helpers.tpl @@ -140,18 +140,10 @@ {{- printf "%s-source-uploader-configmap" .Release.Name | trunc 63 | trimSuffix "-" -}} {{- end -}} -{{- define "configmap.adminVisionName" -}} -{{- printf "%s-admin-vision-configmap" .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- end -}} - {{- define "configmap.extractorSitemapName" -}} {{- printf "%s-extractor-sitemap-configmap" .Release.Name | trunc 63 | trimSuffix "-" -}} {{- end -}} -{{- define "configmap.extractorVisionName" -}} -{{- printf "%s-extractor-vision-configmap" .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- end -}} - # image {{- define "adminBackend.fullImageName" -}} {{- $tag := default .Chart.AppVersion .Values.adminBackend.image.tag -}} diff --git a/infrastructure/rag/templates/_backend_helpers.tpl b/infrastructure/rag/templates/_backend_helpers.tpl index 1b72511b..280d844c 100644 --- a/infrastructure/rag/templates/_backend_helpers.tpl +++ b/infrastructure/rag/templates/_backend_helpers.tpl @@ -103,8 +103,8 @@ basic-auth {{- printf "%s-retriever-configmap" .Release.Name | trunc 63 | trimSuffix "-" -}} {{- end -}} -{{- define "configmap.backendVisionName" -}} -{{- printf "%s-backend-vision-configmap" .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- define "configmap.visionName" -}} +{{- printf "%s-vision-configmap" .Release.Name | trunc 63 | trimSuffix "-" -}} {{- end -}} {{- define "configmap.langfuseName" -}} diff --git a/infrastructure/rag/templates/admin-backend/configmap.yaml b/infrastructure/rag/templates/admin-backend/configmap.yaml index e8dfc8ec..af158c75 100644 --- a/infrastructure/rag/templates/admin-backend/configmap.yaml +++ b/infrastructure/rag/templates/admin-backend/configmap.yaml @@ -42,12 +42,3 @@ data: {{- range $key, $value := .Values.adminBackend.envs.sourceUploader }} {{ $key }}: {{ $value | quote }} {{- end }} ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ template "configmap.adminVisionName" . }} -data: - {{- range $key, $value := .Values.adminBackend.envs.vision }} - {{ $key }}: {{ $value | quote }} - {{- end }} diff --git a/infrastructure/rag/templates/admin-backend/deployment.yaml b/infrastructure/rag/templates/admin-backend/deployment.yaml index 73954274..9de5e21d 100644 --- a/infrastructure/rag/templates/admin-backend/deployment.yaml +++ b/infrastructure/rag/templates/admin-backend/deployment.yaml @@ -118,7 +118,7 @@ spec: - configMapRef: name: {{ template "configmap.sourceUploaderName" . }} - configMapRef: - name: {{ template "configmap.adminVisionName" . }} + name: {{ template "configmap.visionName" . }} - configMapRef: name: {{ template "configmap.retryDecoratorName" . }} - configMapRef: diff --git a/infrastructure/rag/templates/backend/configmap.yaml b/infrastructure/rag/templates/backend/configmap.yaml index 7dd83640..265ce3c3 100644 --- a/infrastructure/rag/templates/backend/configmap.yaml +++ b/infrastructure/rag/templates/backend/configmap.yaml @@ -19,9 +19,9 @@ data: apiVersion: v1 kind: ConfigMap metadata: - name: {{ template "configmap.backendVisionName" . }} + name: {{ template "configmap.visionName" . }} data: - {{- range $key, $value := .Values.backend.envs.vision }} + {{- range $key, $value := .Values.shared.envs.vision }} {{ $key }}: {{ $value | quote }} {{- end }} --- diff --git a/infrastructure/rag/templates/backend/deployment.yaml b/infrastructure/rag/templates/backend/deployment.yaml index a5e75ea1..0e996e7d 100644 --- a/infrastructure/rag/templates/backend/deployment.yaml +++ b/infrastructure/rag/templates/backend/deployment.yaml @@ -110,7 +110,7 @@ spec: - configMapRef: name: {{ template "configmap.retrieverName" . }} - configMapRef: - name: {{ template "configmap.backendVisionName" . }} + name: {{ template "configmap.visionName" . }} - configMapRef: name: {{ template "configmap.rerankerName" . }} - configMapRef: diff --git a/infrastructure/rag/templates/extractor/configmap.yaml b/infrastructure/rag/templates/extractor/configmap.yaml index 3f1819b9..5f02f2c0 100644 --- a/infrastructure/rag/templates/extractor/configmap.yaml +++ b/infrastructure/rag/templates/extractor/configmap.yaml @@ -6,12 +6,3 @@ data: {{- range $key, $value := .Values.extractor.envs.sitemap }} {{ $key }}: {{ $value | quote }} {{- end }} ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ template "configmap.extractorVisionName" . }} -data: - {{- range $key, $value := .Values.extractor.envs.vision }} - {{ $key }}: {{ $value | quote }} - {{- end }} diff --git a/infrastructure/rag/templates/extractor/deployment.yaml b/infrastructure/rag/templates/extractor/deployment.yaml index 26618aee..0160633c 100644 --- a/infrastructure/rag/templates/extractor/deployment.yaml +++ b/infrastructure/rag/templates/extractor/deployment.yaml @@ -113,7 +113,7 @@ spec: - configMapRef: name: {{ template "configmap.extractorSitemapName" . }} - configMapRef: - name: {{ template "configmap.extractorVisionName" . }} + name: {{ template "configmap.visionName" . }} - secretRef: name: {{ template "secret.s3RefName" . }} {{- $hfCacheDir := include "extractor.huggingfaceCacheDir" . }} diff --git a/infrastructure/rag/values.yaml b/infrastructure/rag/values.yaml index 78b56336..f936e6b9 100644 --- a/infrastructure/rag/values.yaml +++ b/infrastructure/rag/values.yaml @@ -196,10 +196,6 @@ backend: RETRIEVER_TABLE_K_DOCUMENTS: 10 RETRIEVER_IMAGE_THRESHOLD: 0.7 RETRIEVER_IMAGE_K_DOCUMENTS: 10 - vision: - VISION_IMAGE_LANE_ENABLED: false - VISION_EMBEDDING_ENABLED: false - VISION_IMAGE_RETRIEVER_ENABLED: false errorMessages: ERROR_MESSAGES_NO_DOCUMENTS_MESSAGE: "I'm sorry, my responses are limited. You must ask the right questions." ERROR_MESSAGES_NO_OR_EMPTY_COLLECTION: "No documents were provided for searching." @@ -394,10 +390,6 @@ adminBackend: sourceUploader: # Large sitemap ingestions (per-page summaries) can take > 1 hour. SOURCE_UPLOADER_TIMEOUT: 3600 - vision: - VISION_IMAGE_LANE_ENABLED: false - VISION_EMBEDDING_ENABLED: false - VISION_IMAGE_RETRIEVER_ENABLED: false extractor: replicaCount: 1 @@ -456,10 +448,6 @@ extractor: # Options: "docusaurus" (default), "astro", "generic" # Note: https://docs.stackit.cloud is built with Astro/Starlight -> use "astro". SITEMAP_PARSER: docusaurus - vision: - VISION_IMAGE_LANE_ENABLED: false - VISION_EMBEDDING_ENABLED: false - VISION_IMAGE_RETRIEVER_ENABLED: false adminFrontend: name: admin-frontend @@ -551,6 +539,10 @@ shared: s3: S3_ENDPOINT: http://rag-minio:9000 S3_BUCKET: documents + vision: + VISION_IMAGE_LANE_ENABLED: false + VISION_EMBEDDING_ENABLED: false + VISION_IMAGE_RETRIEVER_ENABLED: false retryDecorator: RETRY_DECORATOR_MAX_RETRIES: "5" RETRY_DECORATOR_RETRY_BASE_DELAY: "0.5"