fixed imports

This commit is contained in:
ValueOn AG 2026-01-22 21:11:25 +01:00
parent f02ebead7c
commit bb9630d6c4
161 changed files with 1318 additions and 1165 deletions

View file

@ -6,8 +6,8 @@ import re
import time
import base64
from typing import Dict, Any, List, Optional, Tuple
from modules.features.aichat.datamodelFeatureAiChat import PromptPlaceholder, ChatDocument
from modules.features.aichat.serviceExtraction.mainServiceExtraction import ExtractionService
from modules.aichat.datamodelFeatureAiChat import PromptPlaceholder, ChatDocument
from modules.aichat.serviceExtraction.mainServiceExtraction import ExtractionService
from modules.datamodels.datamodelAi import AiCallRequest, AiCallOptions, OperationTypeEnum, PriorityEnum, ProcessingModeEnum
from modules.datamodels.datamodelExtraction import ContentPart, DocumentIntent
from modules.datamodels.datamodelWorkflow import AiResponse, AiResponseMetadata, DocumentData
@ -329,7 +329,7 @@ Respond with ONLY a JSON object in this exact format:
parentOperationId: Optional[str]
) -> AiResponse:
"""Handle IMAGE_GENERATE operation type using image generation path."""
from modules.features.aichat.serviceGeneration.paths.imagePath import ImageGenerationPath
from modules.aichat.serviceGeneration.paths.imagePath import ImageGenerationPath
imagePath = ImageGenerationPath(self.services)
@ -514,7 +514,7 @@ Respond with ONLY a JSON object in this exact format:
)
try:
from modules.features.aichat.serviceGeneration.mainServiceGeneration import GenerationService
from modules.aichat.serviceGeneration.mainServiceGeneration import GenerationService
generationService = GenerationService(self.services)
@ -829,7 +829,7 @@ Respond with ONLY a JSON object in this exact format:
parentOperationId: Optional[str]
) -> AiResponse:
"""Handle code generation using code generation path."""
from modules.features.aichat.serviceGeneration.paths.codePath import CodeGenerationPath
from modules.aichat.serviceGeneration.paths.codePath import CodeGenerationPath
codePath = CodeGenerationPath(self.services)
return await codePath.generateCode(
@ -852,7 +852,7 @@ Respond with ONLY a JSON object in this exact format:
parentOperationId: Optional[str]
) -> AiResponse:
"""Handle document generation using document generation path."""
from modules.features.aichat.serviceGeneration.paths.documentPath import DocumentGenerationPath
from modules.aichat.serviceGeneration.paths.documentPath import DocumentGenerationPath
# Set compression options for document generation
options.compressPrompt = False

View file

@ -14,7 +14,7 @@ import logging
import base64
from typing import Dict, Any, List, Optional
from modules.features.aichat.datamodelFeatureAiChat import ChatDocument
from modules.aichat.datamodelFeatureAiChat import ChatDocument
from modules.datamodels.datamodelExtraction import ContentPart, DocumentIntent
from modules.workflows.processing.shared.stateTools import checkWorkflowStopped

View file

@ -12,7 +12,7 @@ import json
import logging
from typing import Dict, Any, List, Optional
from modules.features.aichat.datamodelFeatureAiChat import ChatDocument
from modules.aichat.datamodelFeatureAiChat import ChatDocument
from modules.datamodels.datamodelExtraction import DocumentIntent
from modules.workflows.processing.shared.stateTools import checkWorkflowStopped

View file

@ -2531,7 +2531,7 @@ CRITICAL:
List of accepted section content types (e.g., ["table", "code_block"])
"""
try:
from modules.features.aichat.serviceGeneration.renderers.registry import getRenderer
from modules.aichat.serviceGeneration.renderers.registry import getRenderer
# Get renderer for this format
renderer = getRenderer(outputFormat, self.services)

View file

@ -231,7 +231,7 @@ CRITICAL:
raise ValueError("Structure has no documents - cannot generate without documents")
# Import renderer registry for format validation (existing infrastructure)
from modules.features.aichat.serviceGeneration.renderers.registry import getRenderer
from modules.aichat.serviceGeneration.renderers.registry import getRenderer
# Validate and fix each document
for doc in documents:

View file

@ -11,10 +11,10 @@ import json
from .subRegistry import ExtractorRegistry, ChunkerRegistry
from .subPipeline import runExtraction
from modules.datamodels.datamodelExtraction import ContentExtracted, ContentPart, MergeStrategy, ExtractionOptions, PartResult, DocumentIntent
from modules.features.aichat.datamodelFeatureAiChat import ChatDocument
from modules.aichat.datamodelFeatureAiChat import ChatDocument
from modules.datamodels.datamodelAi import AiCallResponse, AiCallRequest, AiCallOptions, OperationTypeEnum, AiModelCall
from modules.features.aichat.aicore.aicoreModelRegistry import modelRegistry
from modules.features.aichat.aicore.aicoreModelSelector import modelSelector
from modules.aichat.aicore.aicoreModelRegistry import modelRegistry
from modules.aichat.aicore.aicoreModelSelector import modelSelector
from modules.shared.jsonUtils import stripCodeFences

View file

@ -13,7 +13,7 @@ from modules.datamodels.datamodelAi import AiCallRequest, AiCallOptions, Operati
# Type hint for renderer parameter
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from modules.features.aichat.serviceGeneration.renderers.documentRendererBaseTemplate import BaseRenderer
from modules.aichat.serviceGeneration.renderers.documentRendererBaseTemplate import BaseRenderer
_RendererLike = BaseRenderer
else:
_RendererLike = Any

View file

@ -71,7 +71,7 @@ class ExtractorRegistry:
module_name = file_path.stem
try:
# Import the module
module = importlib.import_module(f".{module_name}", package="modules.features.aichat.serviceExtraction.extractors")
module = importlib.import_module(f".{module_name}", package="modules.aichat.serviceExtraction.extractors")
# Find all extractor classes in the module
for attr_name in dir(module):

View file

@ -6,8 +6,8 @@ import base64
import traceback
from typing import Any, Dict, List, Optional, Callable
from modules.datamodels.datamodelDocument import RenderedDocument
from modules.features.aichat.datamodelFeatureAiChat import ChatDocument
from modules.features.aichat.serviceGeneration.subDocumentUtility import (
from modules.aichat.datamodelFeatureAiChat import ChatDocument
from modules.aichat.serviceGeneration.subDocumentUtility import (
getFileExtension,
getMimeTypeFromExtension,
detectMimeTypeFromContent,
@ -414,7 +414,7 @@ class GenerationService:
continue
# Check output style classification (code/document/image/etc.) from renderer
from modules.features.aichat.serviceGeneration.renderers.registry import getOutputStyle
from modules.aichat.serviceGeneration.renderers.registry import getOutputStyle
outputStyle = getOutputStyle(docFormat)
if outputStyle:
logger.debug(f"Document {doc.get('id', docIndex)} format '{docFormat}' classified as '{outputStyle}' style")
@ -471,8 +471,8 @@ class GenerationService:
Complete document structure with populated elements ready for rendering
"""
try:
from modules.features.aichat.serviceGeneration.subStructureGenerator import StructureGenerator
from modules.features.aichat.serviceGeneration.subContentGenerator import ContentGenerator
from modules.aichat.serviceGeneration.subStructureGenerator import StructureGenerator
from modules.aichat.serviceGeneration.subContentGenerator import ContentGenerator
# Phase 1: Generate structure skeleton
if progressCallback:
@ -537,7 +537,7 @@ class GenerationService:
aiService=None
) -> str:
"""Get adaptive extraction prompt."""
from modules.features.aichat.serviceExtraction.subPromptBuilderExtraction import buildExtractionPrompt
from modules.aichat.serviceExtraction.subPromptBuilderExtraction import buildExtractionPrompt
return await buildExtractionPrompt(
outputFormat=outputFormat,
userPrompt=userPrompt,

View file

@ -920,7 +920,7 @@ CRITICAL:
def _getCodeRenderer(self, fileType: str):
"""Get code renderer for file type."""
from modules.features.aichat.serviceGeneration.renderers.registry import getRenderer
from modules.aichat.serviceGeneration.renderers.registry import getRenderer
# Map file types to renderer formats
formatMap = {

View file

@ -12,7 +12,7 @@ import base64
import re
import traceback
from typing import Dict, Any, Optional, List, Callable
from modules.features.aichat.serviceGeneration.subContentIntegrator import ContentIntegrator
from modules.aichat.serviceGeneration.subContentIntegrator import ContentIntegrator
from modules.workflows.processing.shared.stateTools import checkWorkflowStopped
logger = logging.getLogger(__name__)

View file

@ -4,7 +4,7 @@
from typing import Optional, Any, Union, List, Dict, Callable, Awaitable
from pydantic import BaseModel, Field
from modules.datamodels.datamodelChat import ActionResult
from modules.aichat.datamodelFeatureAiChat import ActionResult
from modules.shared.frontendTypes import FrontendType
from modules.shared.attributeUtils import registerModelLabels

View file

@ -13,9 +13,9 @@ import logging
import json
# Import interfaces and models
from modules.features.aichat.interfaceFeatureAiChat import getInterface as getChatInterface
from modules.aichat.interfaceFeatureAiChat import getInterface as getChatInterface
from modules.auth import getCurrentUser, limiter
from modules.features.aichat.datamodelFeatureAiChat import AutomationDefinition, ChatWorkflow
from modules.aichat.datamodelFeatureAiChat import AutomationDefinition, ChatWorkflow
from modules.datamodels.datamodelPagination import PaginationParams, PaginatedResponse, PaginationMetadata, normalize_pagination_dict
from modules.shared.attributeUtils import getModelAttributeDefinitions
from modules.workflows.automation import executeAutomation

View file

@ -117,7 +117,7 @@ import asyncio
import re
from typing import Optional, Dict, Any, List
from modules.features.aichat.datamodelFeatureAiChat import ChatWorkflow, UserInputRequest, WorkflowModeEnum, ChatLog, ChatDocument
from modules.aichat.datamodelFeatureAiChat import ChatWorkflow, UserInputRequest, WorkflowModeEnum, ChatLog, ChatDocument
from modules.datamodels.datamodelUam import User
from modules.datamodels.datamodelAi import AiCallRequest, AiCallOptions, OperationTypeEnum, ProcessingModeEnum
from modules.datamodels.datamodelDocref import DocumentReferenceList, DocumentItemReference
@ -439,7 +439,7 @@ async def _emit_log_and_event(
# Emit event directly for streaming (using correct signature)
if created_log and event_manager:
try:
from modules.features.aichat.datamodelFeatureAiChat import ChatLog
from modules.aichat.datamodelFeatureAiChat import ChatLog
# Convert to dict if it's a Pydantic model
if hasattr(created_log, "model_dump"):
log_dict = created_log.model_dump()

View file

@ -0,0 +1,176 @@
# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""
Database interface for the Neutralizer feature.
Handles CRUD operations for neutralization configuration and attributes.
"""
import logging
from typing import Dict, List, Any, Optional
from modules.features.neutralizer.datamodelFeatureNeutralizer import (
DataNeutraliserConfig,
DataNeutralizerAttributes,
)
from modules.interfaces.interfaceRbac import getRecordsetWithRBAC
from modules.shared.timeUtils import getUtcTimestamp
logger = logging.getLogger(__name__)
class InterfaceFeatureNeutralizer:
"""Database interface for Neutralizer feature operations"""
def __init__(self, db, currentUser, mandateId: str, userId: str):
"""
Initialize the interface with database connection and user context.
Args:
db: Database connection instance
currentUser: Current user object for RBAC
mandateId: Current mandate ID
userId: Current user ID
"""
self.db = db
self.currentUser = currentUser
self.mandateId = mandateId
self.userId = userId
def getNeutralizationConfig(self) -> Optional[DataNeutraliserConfig]:
"""Get the data neutralization configuration for the current user's mandate"""
try:
# Use RBAC filtering
filteredConfigs = getRecordsetWithRBAC(
self.db,
DataNeutraliserConfig,
self.currentUser,
recordFilter={"mandateId": self.mandateId}
)
if not filteredConfigs:
return None
# Filter out database-specific fields
configDict = filteredConfigs[0]
cleanedConfig = {k: v for k, v in configDict.items() if not k.startswith("_")}
return DataNeutraliserConfig(**cleanedConfig)
except Exception as e:
logger.error(f"Error getting neutralization config: {str(e)}")
return None
def createOrUpdateNeutralizationConfig(
self, configData: Dict[str, Any]
) -> DataNeutraliserConfig:
"""Create or update the data neutralization configuration"""
try:
# Check if config already exists
existingConfig = self.getNeutralizationConfig()
if existingConfig:
# Update existing config
updateData = existingConfig.model_dump()
updateData.update(configData)
updateData["updatedAt"] = getUtcTimestamp()
updatedConfig = DataNeutraliserConfig(**updateData)
self.db.recordModify(
DataNeutraliserConfig, existingConfig.id, updatedConfig
)
return updatedConfig
else:
# Create new config
configData["mandateId"] = self.mandateId
configData["userId"] = self.userId
newConfig = DataNeutraliserConfig(**configData)
createdRecord = self.db.recordCreate(DataNeutraliserConfig, newConfig)
return DataNeutraliserConfig(**createdRecord)
except Exception as e:
logger.error(f"Error creating/updating neutralization config: {str(e)}")
raise ValueError(f"Failed to create/update neutralization config: {str(e)}")
def getNeutralizationAttributes(
self, fileId: Optional[str] = None
) -> List[DataNeutralizerAttributes]:
"""Get neutralization attributes, optionally filtered by file ID"""
try:
filterDict = {"mandateId": self.mandateId}
if fileId:
filterDict["fileId"] = fileId
# Use RBAC filtering
filteredAttributes = getRecordsetWithRBAC(
self.db,
DataNeutralizerAttributes,
self.currentUser,
recordFilter=filterDict
)
# Filter out database-specific fields
cleanedAttributes = []
for attr in filteredAttributes:
cleanedAttr = {k: v for k, v in attr.items() if not k.startswith("_")}
cleanedAttributes.append(cleanedAttr)
return [
DataNeutralizerAttributes(**attr)
for attr in cleanedAttributes
]
except Exception as e:
logger.error(f"Error getting neutralization attributes: {str(e)}")
return []
def deleteNeutralizationAttributes(self, fileId: str) -> bool:
"""Delete all neutralization attributes for a specific file"""
try:
attributes = self.db.getRecordset(
DataNeutralizerAttributes,
recordFilter={"mandateId": self.mandateId, "fileId": fileId},
)
for attribute in attributes:
self.db.recordDelete(DataNeutralizerAttributes, attribute["id"])
logger.info(
f"Deleted {len(attributes)} neutralization attributes for file {fileId}"
)
return True
except Exception as e:
logger.error(f"Error deleting neutralization attributes: {str(e)}")
return False
def getAttributeById(self, attributeId: str) -> Optional[Dict[str, Any]]:
"""Get a single neutralization attribute by ID"""
try:
attributes = self.db.getRecordset(
DataNeutralizerAttributes,
recordFilter={"mandateId": self.mandateId, "id": attributeId}
)
if attributes:
return attributes[0]
return None
except Exception as e:
logger.error(f"Error getting attribute by ID: {str(e)}")
return None
def getInterface(db, currentUser, mandateId: str, userId: str) -> InterfaceFeatureNeutralizer:
"""
Factory function to create a Neutralizer interface instance.
Args:
db: Database connection
currentUser: Current user for RBAC
mandateId: Current mandate ID
userId: Current user ID
Returns:
InterfaceFeatureNeutralizer instance
"""
return InterfaceFeatureNeutralizer(db, currentUser, mandateId, userId)

View file

@ -14,6 +14,7 @@ import json
from typing import Dict, List, Any, Optional
from modules.features.neutralizer.datamodelFeatureNeutralizer import DataNeutraliserConfig, DataNeutralizerAttributes
from modules.features.neutralizer.interfaceFeatureNeutralizer import InterfaceFeatureNeutralizer
# Import all necessary classes and functions for neutralization
from .subProcessCommon import CommonUtils, NeutralizationResult, NeutralizationAttribute
@ -35,9 +36,19 @@ class NeutralizationService:
NamesToParse: List of names to parse and replace (case-insensitive)
"""
self.services = serviceCenter
self.interfaceDbApp = serviceCenter.interfaceDbApp
self.interfaceDbComponent = serviceCenter.interfaceDbComponent
# Create feature-specific interface for neutralizer DB operations
self.interfaceNeutralizer: InterfaceFeatureNeutralizer = None
if serviceCenter and serviceCenter.interfaceDbApp:
dbApp = serviceCenter.interfaceDbApp
self.interfaceNeutralizer = InterfaceFeatureNeutralizer(
db=dbApp.db,
currentUser=dbApp.currentUser,
mandateId=dbApp.mandateId,
userId=dbApp.userId
)
# Initialize anonymization processors
self.NamesToParse = NamesToParse or []
self.textProcessor = TextProcessor(NamesToParse)
@ -47,15 +58,15 @@ class NeutralizationService:
def getConfig(self) -> Optional[DataNeutraliserConfig]:
"""Get the neutralization configuration for the current user's mandate"""
if not self.interfaceDbApp:
if not self.interfaceNeutralizer:
return None
return self.interfaceDbApp.getNeutralizationConfig()
return self.interfaceNeutralizer.getNeutralizationConfig()
def saveConfig(self, config_data: Dict[str, Any]) -> DataNeutraliserConfig:
def saveConfig(self, configData: Dict[str, Any]) -> DataNeutraliserConfig:
"""Save or update the neutralization configuration"""
if not self.interfaceDbApp:
if not self.interfaceNeutralizer:
raise ValueError("User context required for saving configuration")
return self.interfaceDbApp.createOrUpdateNeutralizationConfig(config_data)
return self.interfaceNeutralizer.createOrUpdateNeutralizationConfig(configData)
# Public API: process text or file
@ -125,44 +136,37 @@ class NeutralizationService:
return result
def resolveText(self, text: str) -> str:
if not self.interfaceDbApp:
if not self.interfaceNeutralizer:
return text
try:
placeholder_pattern = r'\[([a-z]+)\.([a-f0-9-]{36})\]'
matches = re.findall(placeholder_pattern, text)
resolved_text = text
for placeholder_type, uid in matches:
attributes = self.interfaceDbApp.db.getRecordset(
DataNeutralizerAttributes,
recordFilter={
"mandateId": self.interfaceDbApp.mandateId,
"id": uid
}
)
if attributes:
attribute = attributes[0]
placeholder = f"[{placeholder_type}.{uid}]"
resolved_text = resolved_text.replace(placeholder, attribute["originalText"])
return resolved_text
placeholderPattern = r'\[([a-z]+)\.([a-f0-9-]{36})\]'
matches = re.findall(placeholderPattern, text)
resolvedText = text
for placeholderType, uid in matches:
attribute = self.interfaceNeutralizer.getAttributeById(uid)
if attribute:
placeholder = f"[{placeholderType}.{uid}]"
resolvedText = resolvedText.replace(placeholder, attribute["originalText"])
return resolvedText
except Exception:
return text
def getAttributes(self) -> List[DataNeutralizerAttributes]:
"""Get all neutralization attributes for the current user's mandate"""
if not self.interfaceDbApp:
if not self.interfaceNeutralizer:
return []
try:
# Use the interface method which properly converts dicts to objects
return self.interfaceDbApp.getNeutralizationAttributes()
return self.interfaceNeutralizer.getNeutralizationAttributes()
except Exception as e:
logger.error(f"Error getting neutralization attributes: {str(e)}")
return []
def deleteNeutralizationAttributes(self, fileId: str) -> bool:
"""Delete neutralization attributes for a specific file"""
if not self.interfaceDbApp:
if not self.interfaceNeutralizer:
return False
return self.interfaceDbApp.deleteNeutralizationAttributes(fileId)
return self.interfaceNeutralizer.deleteNeutralizationAttributes(fileId)
def _reloadNamesFromConfig(self) -> None:
"""Reload names from config and update processors"""

View file

@ -10,8 +10,8 @@ import time
logger = logging.getLogger(__name__)
from modules.features.aichat.aicore.aicoreModelRegistry import modelRegistry
from modules.features.aichat.aicore.aicoreModelSelector import modelSelector
from modules.aichat.aicore.aicoreModelRegistry import modelRegistry
from modules.aichat.aicore.aicoreModelSelector import modelSelector
from modules.datamodels.datamodelAi import (
AiModel,
AiCallOptions,

View file

@ -36,10 +36,6 @@ from modules.datamodels.datamodelRbac import (
)
from modules.datamodels.datamodelUam import AccessLevel
from modules.datamodels.datamodelSecurity import Token, AuthEvent, TokenStatus
from modules.features.neutralizer.datamodelFeatureNeutralizer import (
DataNeutraliserConfig,
DataNeutralizerAttributes,
)
from modules.datamodels.datamodelPagination import PaginationParams, PaginatedResult
from modules.datamodels.datamodelMembership import (
UserMandate,
@ -2040,115 +2036,6 @@ class AppObjects:
logger.error(f"Error during logout: {str(e)}")
raise
# Neutralization methods
def getNeutralizationConfig(self) -> Optional[DataNeutraliserConfig]:
"""Get the data neutralization configuration for the current user's mandate"""
try:
# Use RBAC filtering
filtered_configs = getRecordsetWithRBAC(self.db,
DataNeutraliserConfig,
self.currentUser,
recordFilter={"mandateId": self.mandateId}
)
if not filtered_configs:
return None
# Filter out database-specific fields
configDict = filtered_configs[0]
cleanedConfig = {k: v for k, v in configDict.items() if not k.startswith("_")}
return DataNeutraliserConfig(**cleanedConfig)
except Exception as e:
logger.error(f"Error getting neutralization config: {str(e)}")
return None
def createOrUpdateNeutralizationConfig(
self, config_data: Dict[str, Any]
) -> DataNeutraliserConfig:
"""Create or update the data neutralization configuration"""
try:
# Check if config already exists
existing_config = self.getNeutralizationConfig()
if existing_config:
# Update existing config
update_data = existing_config.model_dump()
update_data.update(config_data)
update_data["updatedAt"] = getUtcTimestamp()
updated_config = DataNeutraliserConfig(**update_data)
self.db.recordModify(
DataNeutraliserConfig, existing_config.id, updated_config
)
return updated_config
else:
# Create new config
config_data["mandateId"] = self.mandateId
config_data["userId"] = self.userId
new_config = DataNeutraliserConfig(**config_data)
created_record = self.db.recordCreate(DataNeutraliserConfig, new_config)
return DataNeutraliserConfig(**created_record)
except Exception as e:
logger.error(f"Error creating/updating neutralization config: {str(e)}")
raise ValueError(f"Failed to create/update neutralization config: {str(e)}")
def getNeutralizationAttributes(
self, file_id: Optional[str] = None
) -> List[DataNeutralizerAttributes]:
"""Get neutralization attributes, optionally filtered by file ID"""
try:
filter_dict = {"mandateId": self.mandateId}
if file_id:
filter_dict["fileId"] = file_id
# Use RBAC filtering
filtered_attributes = getRecordsetWithRBAC(self.db,
DataNeutralizerAttributes,
self.currentUser,
recordFilter=filter_dict
)
# Filter out database-specific fields
cleaned_attributes = []
for attr in filtered_attributes:
cleanedAttr = {k: v for k, v in attr.items() if not k.startswith("_")}
cleaned_attributes.append(cleanedAttr)
return [
DataNeutralizerAttributes(**attr)
for attr in cleaned_attributes
]
except Exception as e:
logger.error(f"Error getting neutralization attributes: {str(e)}")
return []
def deleteNeutralizationAttributes(self, file_id: str) -> bool:
"""Delete all neutralization attributes for a specific file"""
try:
attributes = self.db.getRecordset(
DataNeutralizerAttributes,
recordFilter={"mandateId": self.mandateId, "fileId": file_id},
)
for attribute in attributes:
self.db.recordDelete(DataNeutralizerAttributes, attribute["id"])
logger.info(
f"Deleted {len(attributes)} neutralization attributes for file {file_id}"
)
return True
except Exception as e:
logger.error(f"Error deleting neutralization attributes: {str(e)}")
return False
# RBAC CRUD Methods
def createAccessRule(self, accessRule: AccessRule) -> AccessRule:

View file

@ -11,7 +11,7 @@ from fastapi import status
import logging
# Import interfaces and models from feature containers
import modules.features.aichat.interfaceFeatureAiChat as interfaceDbChat
import modules.aichat.interfaceFeatureAiChat as interfaceDbChat
from modules.auth import limiter, getRequestContext, requireSysAdmin, RequestContext
from modules.datamodels.datamodelUam import User
@ -76,7 +76,7 @@ async def sync_all_automation_events(
This will register/remove events based on active flags.
"""
try:
from modules.features.aichat.interfaceFeatureAiChat import getInterface as getChatInterface
from modules.aichat.interfaceFeatureAiChat import getInterface as getChatInterface
from modules.interfaces.interfaceDbApp import getRootInterface
from modules.workflows.automation import syncAutomationEvents

View file

@ -14,12 +14,12 @@ from fastapi import APIRouter, HTTPException, Depends, Body, Path, Query, Respon
from modules.auth import limiter, getCurrentUser
# Import interfaces from feature containers
import modules.features.aichat.interfaceFeatureAiChat as interfaceDbChat
from modules.features.aichat.interfaceFeatureAiChat import getInterface
import modules.aichat.interfaceFeatureAiChat as interfaceDbChat
from modules.aichat.interfaceFeatureAiChat import getInterface
from modules.interfaces.interfaceRbac import getRecordsetWithRBAC
# Import models from feature containers
from modules.features.aichat.datamodelFeatureAiChat import (
from modules.aichat.datamodelFeatureAiChat import (
ChatWorkflow,
ChatMessage,
ChatLog,

View file

@ -0,0 +1,54 @@
# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""
Password utility functions for secure password handling.
Uses Argon2 for password hashing.
"""
from typing import Optional
from passlib.context import CryptContext
# Password hashing context using Argon2
_pwdContext = CryptContext(schemes=["argon2"], deprecated="auto")
def hashPassword(password: str) -> str:
"""
Hash a password using Argon2.
Args:
password: Plain text password to hash
Returns:
Hashed password string
"""
return _pwdContext.hash(password)
def verifyPassword(plainPassword: str, hashedPassword: str) -> bool:
"""
Verify a plain password against a hashed password.
Args:
plainPassword: Plain text password to verify
hashedPassword: Hashed password to compare against
Returns:
True if password matches, False otherwise
"""
return _pwdContext.verify(plainPassword, hashedPassword)
def getPasswordHash(password: Optional[str]) -> Optional[str]:
"""
Hash a password, returning None if password is None.
Args:
password: Plain text password or None
Returns:
Hashed password or None if input was None
"""
if password is None:
return None
return _pwdContext.hash(password)

View file

@ -19,7 +19,7 @@ import logging
from modules.datamodels.datamodelUam import User
if TYPE_CHECKING:
from modules.features.aichat.datamodelFeatureAiChat import ChatWorkflow
from modules.aichat.datamodelFeatureAiChat import ChatWorkflow
logger = logging.getLogger(__name__)

View file

@ -3,7 +3,7 @@
import logging
from typing import Dict, Any, List, Optional
from modules.datamodels.datamodelUam import User, UserConnection
from modules.features.aichat.datamodelFeatureAiChat import ChatDocument, ChatMessage, ChatStat, ChatLog
from modules.aichat.datamodelFeatureAiChat import ChatDocument, ChatMessage, ChatStat, ChatLog
from modules.datamodels.datamodelAi import AiCallOptions, OperationTypeEnum, PriorityEnum, ProcessingModeEnum
from modules.shared.progressLogger import ProgressLogger

View file

@ -161,7 +161,7 @@ class UtilsService:
Mirrors storeDebugMessageAndDocuments() in modules.interfaces.interfaceDbChat.
"""
try:
from modules.features.aichat.interfaceFeatureAiChat import storeDebugMessageAndDocuments as _storeDebugMessageAndDocuments
from modules.aichat.interfaceFeatureAiChat import storeDebugMessageAndDocuments as _storeDebugMessageAndDocuments
_storeDebugMessageAndDocuments(message, currentUser)
except Exception:
# Silent fail to never break main flow

View file

@ -0,0 +1,19 @@
# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""
Shared utilities module.
Contains common utilities used across the gateway application.
"""
from . import jsonUtils
from . import timeUtils
from . import attributeUtils
from . import frontendTypes
from . import configuration
from . import eventManagement
from . import auditLogger
from . import debugLogger
from . import progressLogger
from . import callbackRegistry
from . import jsonContinuation
from . import dbMultiTenantOptimizations

View file

@ -12,7 +12,7 @@ import logging
import json
from typing import Dict, Any, Optional
from modules.features.aichat.datamodelFeatureAiChat import ChatWorkflow, UserInputRequest, WorkflowModeEnum, AutomationDefinition
from modules.aichat.datamodelFeatureAiChat import ChatWorkflow, UserInputRequest, WorkflowModeEnum, AutomationDefinition
from modules.datamodels.datamodelUam import User
from modules.shared.timeUtils import getUtcTimestamp
from modules.shared.eventManagement import eventManager

View file

@ -3,7 +3,7 @@
import logging
from typing import Dict, Any
from modules.features.aichat.datamodelFeatureAiChat import ActionResult
from modules.aichat.datamodelFeatureAiChat import ActionResult
logger = logging.getLogger(__name__)

View file

@ -4,7 +4,7 @@
import logging
import time
from typing import Dict, Any, Optional, List
from modules.features.aichat.datamodelFeatureAiChat import ActionResult, ActionDocument
from modules.aichat.datamodelFeatureAiChat import ActionResult, ActionDocument
from modules.datamodels.datamodelExtraction import ContentPart
from modules.datamodels.datamodelAi import AiCallOptions, OperationTypeEnum, PriorityEnum, ProcessingModeEnum
from modules.datamodels.datamodelWorkflow import AiResponse, DocumentData

View file

@ -4,7 +4,7 @@
import logging
import time
from typing import Dict, Any, Optional, List
from modules.features.aichat.datamodelFeatureAiChat import ActionResult, ActionDocument
from modules.aichat.datamodelFeatureAiChat import ActionResult, ActionDocument
from modules.datamodels.datamodelExtraction import ContentPart
from modules.datamodels.datamodelAi import AiCallOptions, OperationTypeEnum, PriorityEnum, ProcessingModeEnum
from modules.datamodels.datamodelWorkflow import AiResponse, DocumentData

View file

@ -5,7 +5,7 @@ import logging
import time
import json
from typing import Dict, Any, List, Optional
from modules.features.aichat.datamodelFeatureAiChat import ActionResult, ActionDocument
from modules.aichat.datamodelFeatureAiChat import ActionResult, ActionDocument
from modules.datamodels.datamodelAi import AiCallOptions
from modules.datamodels.datamodelExtraction import ContentPart

View file

@ -3,7 +3,7 @@
import logging
from typing import Dict, Any
from modules.features.aichat.datamodelFeatureAiChat import ActionResult
from modules.aichat.datamodelFeatureAiChat import ActionResult
logger = logging.getLogger(__name__)

Some files were not shown because too many files have changed in this diff Show more