Merge pull request #108 from valueonag/feat/code-editor

vector fixes
This commit is contained in:
Patrick Motsch 2026-03-18 00:34:04 +01:00 committed by GitHub
commit 15dde154f5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 66 additions and 19 deletions

View file

@ -177,7 +177,7 @@ def _get_cached_connector(
oldest_key = _connector_cache_order.pop(0) oldest_key = _connector_cache_order.pop(0)
if oldest_key in _connector_cache: if oldest_key in _connector_cache:
try: try:
_connector_cache[oldest_key].close() _connector_cache[oldest_key].close(forceClose=True)
except Exception as e: except Exception as e:
logger.warning(f"Error closing evicted connector: {e}") logger.warning(f"Error closing evicted connector: {e}")
del _connector_cache[oldest_key] del _connector_cache[oldest_key]
@ -189,6 +189,7 @@ def _get_cached_connector(
dbPort=dbPort, dbPort=dbPort,
userId=userId, userId=userId,
) )
_connector_cache[key]._isCachedShared = True
_connector_cache_order.append(key) _connector_cache_order.append(key)
conn = _connector_cache[key] conn = _connector_cache[key]
# Set request-scoped userId via contextvar (avoids mutating shared connector) # Set request-scoped userId via contextvar (avoids mutating shared connector)
@ -225,6 +226,7 @@ class DatabaseConnector:
# Initialize database system first (creates database if needed) # Initialize database system first (creates database if needed)
self.connection = None self.connection = None
self._isCachedShared = False
self.initDbSystem() self.initDbSystem()
# No caching needed with proper database - PostgreSQL handles performance # 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}") logger.error(f"Error in semantic search on {table}: {e}")
return [] return []
def close(self): def close(self, forceClose: bool = False):
"""Close the database connection.""" """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 ( if (
hasattr(self, "connection") hasattr(self, "connection")
and self.connection and self.connection

View file

@ -1743,11 +1743,15 @@ class ComponentObjects:
logger.error("No user ID provided for voice settings") logger.error("No user ID provided for voice settings")
return None 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, filteredSettings = getRecordsetWithRBAC(self.db,
VoiceSettings, VoiceSettings,
self.currentUser, self.currentUser,
recordFilter={"userId": targetUserId}, recordFilter=recordFilter,
mandateId=self.mandateId mandateId=self.mandateId
) )

View file

@ -2239,19 +2239,53 @@ def _registerCoreTools(registry: ToolRegistry, services):
if not voiceName: if not voiceName:
try: try:
from modules.interfaces import interfaceDbManagement
featureInstanceId = context.get("featureInstanceId", "") featureInstanceId = context.get("featureInstanceId", "")
userId = context.get("userId", "") userId = context.get("userId", "")
if featureInstanceId and userId: if userId:
dbMgmt = services.chat.interfaceDbApp if hasattr(services.chat, "interfaceDbApp") else None dbMgmt = interfaceDbManagement.getInterface(
if dbMgmt and hasattr(dbMgmt, "getVoiceSettings"): services.user,
vs = dbMgmt.getVoiceSettings(userId) mandateId=mandateId or None,
featureInstanceId=featureInstanceId or None,
)
vs = dbMgmt.getVoiceSettings(userId) if dbMgmt and hasattr(dbMgmt, "getVoiceSettings") else None
if vs: if vs:
voiceMap = {} voiceMap = {}
if hasattr(vs, "ttsVoiceMap") and vs.ttsVoiceMap: if hasattr(vs, "ttsVoiceMap") and vs.ttsVoiceMap:
voiceMap = vs.ttsVoiceMap if isinstance(vs.ttsVoiceMap, dict) else {} 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] selectedKey = None
logger.info(f"textToSpeech: using configured voice '{voiceName}' for {language}") 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: elif hasattr(vs, "ttsVoice") and vs.ttsVoice and hasattr(vs, "ttsLanguage") and vs.ttsLanguage == language:
voiceName = vs.ttsVoice voiceName = vs.ttsVoice
except Exception as prefErr: except Exception as prefErr: