From 9b741a0a286ec68d1a3d4624479cce8ad070c88b Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Wed, 18 Mar 2026 00:33:38 +0100
Subject: [PATCH] vector fixes
---
modules/connectors/connectorDbPostgre.py | 15 ++++-
modules/interfaces/interfaceDbManagement.py | 10 +++-
.../services/serviceAgent/mainServiceAgent.py | 60 +++++++++++++++----
3 files changed, 66 insertions(+), 19 deletions(-)
diff --git a/modules/connectors/connectorDbPostgre.py b/modules/connectors/connectorDbPostgre.py
index 77689ae5..83517f31 100644
--- a/modules/connectors/connectorDbPostgre.py
+++ b/modules/connectors/connectorDbPostgre.py
@@ -177,7 +177,7 @@ def _get_cached_connector(
oldest_key = _connector_cache_order.pop(0)
if oldest_key in _connector_cache:
try:
- _connector_cache[oldest_key].close()
+ _connector_cache[oldest_key].close(forceClose=True)
except Exception as e:
logger.warning(f"Error closing evicted connector: {e}")
del _connector_cache[oldest_key]
@@ -189,6 +189,7 @@ def _get_cached_connector(
dbPort=dbPort,
userId=userId,
)
+ _connector_cache[key]._isCachedShared = True
_connector_cache_order.append(key)
conn = _connector_cache[key]
# Set request-scoped userId via contextvar (avoids mutating shared connector)
@@ -225,6 +226,7 @@ class DatabaseConnector:
# Initialize database system first (creates database if needed)
self.connection = None
+ self._isCachedShared = False
self.initDbSystem()
# No caching needed with proper database - PostgreSQL handles performance
@@ -1159,8 +1161,15 @@ class DatabaseConnector:
logger.error(f"Error in semantic search on {table}: {e}")
return []
- def close(self):
- """Close the database connection."""
+ def close(self, forceClose: bool = False):
+ """Close the database connection.
+
+ Shared cached connectors are intentionally kept open unless forceClose=True.
+ This prevents accidental shutdown from interface __del__ methods while
+ other requests are still using the same cached connector instance.
+ """
+ if self._isCachedShared and not forceClose:
+ return
if (
hasattr(self, "connection")
and self.connection
diff --git a/modules/interfaces/interfaceDbManagement.py b/modules/interfaces/interfaceDbManagement.py
index 4ddcb972..9c266aac 100644
--- a/modules/interfaces/interfaceDbManagement.py
+++ b/modules/interfaces/interfaceDbManagement.py
@@ -1742,12 +1742,16 @@ class ComponentObjects:
if not targetUserId:
logger.error("No user ID provided for voice settings")
return None
-
- # Get voice settings for the user, filtered by RBAC
+
+ recordFilter: Dict[str, Any] = {"userId": targetUserId}
+ if self.featureInstanceId:
+ recordFilter["featureInstanceId"] = self.featureInstanceId
+
+ # Get voice settings for the user (scoped to current feature instance if available), filtered by RBAC
filteredSettings = getRecordsetWithRBAC(self.db,
VoiceSettings,
self.currentUser,
- recordFilter={"userId": targetUserId},
+ recordFilter=recordFilter,
mandateId=self.mandateId
)
diff --git a/modules/serviceCenter/services/serviceAgent/mainServiceAgent.py b/modules/serviceCenter/services/serviceAgent/mainServiceAgent.py
index c642a2fa..2de2ebd4 100644
--- a/modules/serviceCenter/services/serviceAgent/mainServiceAgent.py
+++ b/modules/serviceCenter/services/serviceAgent/mainServiceAgent.py
@@ -2239,21 +2239,55 @@ def _registerCoreTools(registry: ToolRegistry, services):
if not voiceName:
try:
+ from modules.interfaces import interfaceDbManagement
featureInstanceId = context.get("featureInstanceId", "")
userId = context.get("userId", "")
- if featureInstanceId and userId:
- dbMgmt = services.chat.interfaceDbApp if hasattr(services.chat, "interfaceDbApp") else None
- if dbMgmt and hasattr(dbMgmt, "getVoiceSettings"):
- vs = dbMgmt.getVoiceSettings(userId)
- if vs:
- voiceMap = {}
- if hasattr(vs, "ttsVoiceMap") and vs.ttsVoiceMap:
- voiceMap = vs.ttsVoiceMap if isinstance(vs.ttsVoiceMap, dict) else {}
- if language in voiceMap:
- voiceName = voiceMap[language].get("voiceName") if isinstance(voiceMap[language], dict) else voiceMap[language]
- logger.info(f"textToSpeech: using configured voice '{voiceName}' for {language}")
- elif hasattr(vs, "ttsVoice") and vs.ttsVoice and hasattr(vs, "ttsLanguage") and vs.ttsLanguage == language:
- voiceName = vs.ttsVoice
+ if userId:
+ dbMgmt = interfaceDbManagement.getInterface(
+ services.user,
+ mandateId=mandateId or None,
+ featureInstanceId=featureInstanceId or None,
+ )
+ vs = dbMgmt.getVoiceSettings(userId) if dbMgmt and hasattr(dbMgmt, "getVoiceSettings") else None
+ if vs:
+ voiceMap = {}
+ if hasattr(vs, "ttsVoiceMap") and vs.ttsVoiceMap:
+ voiceMap = vs.ttsVoiceMap if isinstance(vs.ttsVoiceMap, dict) else {}
+
+ selectedKey = None
+ selectedVoiceEntry = None
+ baseLanguage = language.split("-")[0].lower() if isinstance(language, str) and language else ""
+
+ # 1) Exact match first (e.g. de-DE)
+ if isinstance(language, str) and language in voiceMap:
+ selectedKey = language
+ selectedVoiceEntry = voiceMap[language]
+
+ # 2) Match short language key (e.g. de)
+ if selectedVoiceEntry is None and baseLanguage and baseLanguage in voiceMap:
+ selectedKey = baseLanguage
+ selectedVoiceEntry = voiceMap[baseLanguage]
+
+ # 3) Match by same language family (e.g. de-CH -> de-DE mapping)
+ if selectedVoiceEntry is None and baseLanguage:
+ for mapKey, mapValue in voiceMap.items():
+ mapKeyNorm = str(mapKey).lower()
+ if mapKeyNorm == baseLanguage or mapKeyNorm.startswith(f"{baseLanguage}-"):
+ selectedKey = str(mapKey)
+ selectedVoiceEntry = mapValue
+ break
+
+ if selectedVoiceEntry is not None:
+ voiceName = (
+ selectedVoiceEntry.get("voiceName")
+ if isinstance(selectedVoiceEntry, dict)
+ else selectedVoiceEntry
+ )
+ logger.info(
+ f"textToSpeech: using configured voice '{voiceName}' for requested language '{language}' (matched key '{selectedKey}')"
+ )
+ elif hasattr(vs, "ttsVoice") and vs.ttsVoice and hasattr(vs, "ttsLanguage") and vs.ttsLanguage == language:
+ voiceName = vs.ttsVoice
except Exception as prefErr:
logger.debug(f"textToSpeech: could not load voice preferences: {prefErr}")