diff --git a/modules/aicore/aicorePluginPerplexity.py b/modules/aicore/aicorePluginPerplexity.py index e751f1e9..e6d1ba10 100644 --- a/modules/aicore/aicorePluginPerplexity.py +++ b/modules/aicore/aicorePluginPerplexity.py @@ -6,7 +6,7 @@ from typing import List from fastapi import HTTPException from modules.shared.configuration import APP_CONFIG from .aicoreBase import BaseConnectorAi -from modules.datamodels.datamodelAi import AiModel, PriorityEnum, ProcessingModeEnum, OperationTypeEnum, AiModelCall, AiModelResponse, createOperationTypeRatings, AiCallPromptWebSearch, AiCallPromptWebCrawl +from modules.datamodels.datamodelAi import AiModel, PriorityEnum, ProcessingModeEnum, OperationTypeEnum, AiModelCall, AiModelResponse, createOperationTypeRatings, AiCallPromptWebSearch, AiCallPromptWebCrawl, AiCallOptions from modules.datamodels.datamodelTools import CountryCodes # Configure logger diff --git a/modules/datamodels/datamodelChat.py b/modules/datamodels/datamodelChat.py index c2838ad3..328bee22 100644 --- a/modules/datamodels/datamodelChat.py +++ b/modules/datamodels/datamodelChat.py @@ -14,11 +14,11 @@ class ChatStat(BaseModel): id: str = Field( default_factory=lambda: str(uuid.uuid4()), description="Primary key" ) - mandateId: str = Field( - description="ID of the mandate this stat belongs to" + mandateId: Optional[str] = Field( + default="", description="ID of the mandate this stat belongs to" ) - featureInstanceId: str = Field( - description="ID of the feature instance this stat belongs to" + featureInstanceId: Optional[str] = Field( + default="", description="ID of the feature instance this stat belongs to" ) workflowId: Optional[str] = Field( None, description="Foreign key to workflow (for workflow stats)" @@ -57,11 +57,11 @@ class ChatLog(BaseModel): id: str = Field( default_factory=lambda: str(uuid.uuid4()), description="Primary key" ) - mandateId: str = Field( - description="ID of the mandate this log belongs to" + mandateId: Optional[str] = Field( + default="", description="ID of the mandate this log belongs to" ) - featureInstanceId: str = Field( - description="ID of the feature instance this log belongs to" + featureInstanceId: Optional[str] = Field( + default="", description="ID of the feature instance this log belongs to" ) workflowId: str = Field(description="Foreign key to workflow") message: str = Field(description="Log message") @@ -110,11 +110,11 @@ class ChatDocument(BaseModel): id: str = Field( default_factory=lambda: str(uuid.uuid4()), description="Primary key" ) - mandateId: str = Field( - description="ID of the mandate this document belongs to" + mandateId: Optional[str] = Field( + default="", description="ID of the mandate this document belongs to" ) - featureInstanceId: str = Field( - description="ID of the feature instance this document belongs to" + featureInstanceId: Optional[str] = Field( + default="", description="ID of the feature instance this document belongs to" ) messageId: str = Field(description="Foreign key to message") fileId: str = Field(description="Foreign key to file") @@ -224,11 +224,11 @@ class ChatMessage(BaseModel): id: str = Field( default_factory=lambda: str(uuid.uuid4()), description="Primary key" ) - mandateId: str = Field( - description="ID of the mandate this message belongs to" + mandateId: Optional[str] = Field( + default="", description="ID of the mandate this message belongs to" ) - featureInstanceId: str = Field( - description="ID of the feature instance this message belongs to" + featureInstanceId: Optional[str] = Field( + default="", description="ID of the feature instance this message belongs to" ) workflowId: str = Field(description="Foreign key to workflow") parentMessageId: Optional[str] = Field( @@ -327,8 +327,8 @@ registerModelLabels( class ChatWorkflow(BaseModel): id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Primary key", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False}) - mandateId: str = Field(description="ID of the mandate this workflow belongs to", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False}) - featureInstanceId: str = Field(description="ID of the feature instance this workflow belongs to", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False}) + mandateId: Optional[str] = Field(default="", description="ID of the mandate this workflow belongs to", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False}) + featureInstanceId: Optional[str] = Field(default="", description="ID of the feature instance this workflow belongs to", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False}) status: str = Field(default="running", description="Current status of the workflow", json_schema_extra={"frontend_type": "select", "frontend_readonly": False, "frontend_required": False, "frontend_options": [ {"value": "running", "label": {"en": "Running", "fr": "En cours"}}, {"value": "completed", "label": {"en": "Completed", "fr": "Terminé"}}, diff --git a/modules/datamodels/datamodelFiles.py b/modules/datamodels/datamodelFiles.py index 07880f3d..f1b07eb3 100644 --- a/modules/datamodels/datamodelFiles.py +++ b/modules/datamodels/datamodelFiles.py @@ -12,8 +12,8 @@ import base64 class FileItem(BaseModel): id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Primary key", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False}) - mandateId: str = Field(description="ID of the mandate this file belongs to", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False}) - featureInstanceId: str = Field(description="ID of the feature instance this file belongs to", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False}) + mandateId: Optional[str] = Field(default="", description="ID of the mandate this file belongs to", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False}) + featureInstanceId: Optional[str] = Field(default="", description="ID of the feature instance this file belongs to", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False}) fileName: str = Field(description="Name of the file", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": True}) mimeType: str = Field(description="MIME type of the file", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False}) fileHash: str = Field(description="Hash of the file", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False}) diff --git a/modules/interfaces/interfaceDbManagement.py b/modules/interfaces/interfaceDbManagement.py index b514cbf3..a26e8c98 100644 --- a/modules/interfaces/interfaceDbManagement.py +++ b/modules/interfaces/interfaceDbManagement.py @@ -989,8 +989,9 @@ class ComponentObjects: fileHash = hashlib.sha256(content).hexdigest() # Use mandateId and featureInstanceId from context for proper data isolation - mandateId = self.mandateId - featureInstanceId = self.featureInstanceId + # Convert None to empty string to satisfy Pydantic validation + mandateId = self.mandateId or "" + featureInstanceId = self.featureInstanceId or "" # Create FileItem instance fileItem = FileItem( diff --git a/modules/interfaces/interfaceRbac.py b/modules/interfaces/interfaceRbac.py index 6b432a2b..a99bfd0b 100644 --- a/modules/interfaces/interfaceRbac.py +++ b/modules/interfaces/interfaceRbac.py @@ -62,7 +62,6 @@ def getRecordsetWithRBAC( # SysAdmin bypass: SysAdmin users have full access to all tables isSysAdmin = getattr(currentUser, 'isSysAdmin', False) if isSysAdmin: - logger.debug(f"SysAdmin user {currentUser.id} bypassing RBAC for table {table}") # Direct access without RBAC filtering # Note: getRecordset doesn't support orderBy/limit - these are only used in RBAC path return connector.getRecordset(modelClass, recordFilter=recordFilter) diff --git a/modules/routes/routeDataConnections.py b/modules/routes/routeDataConnections.py index 210e0522..2dade569 100644 --- a/modules/routes/routeDataConnections.py +++ b/modules/routes/routeDataConnections.py @@ -20,6 +20,7 @@ import math from modules.datamodels.datamodelUam import User, UserConnection, AuthAuthority, ConnectionStatus from modules.datamodels.datamodelSecurity import Token from modules.auth import getCurrentUser, limiter +from modules.auth.tokenRefreshService import token_refresh_service from modules.datamodels.datamodelPagination import PaginationParams, PaginatedResponse, PaginationMetadata, normalize_pagination_dict from modules.interfaces.interfaceDbApp import getInterface from modules.shared.timeUtils import getUtcTimestamp, parseTimestamp diff --git a/modules/routes/routeDataFiles.py b/modules/routes/routeDataFiles.py index 66345ce5..1a84b7e4 100644 --- a/modules/routes/routeDataFiles.py +++ b/modules/routes/routeDataFiles.py @@ -136,15 +136,13 @@ async def upload_file( else: # new_file message = "File uploaded successfully" - # If workflowId is provided, update the file information - if workflowId: - updateData = {"workflowId": workflowId} - managementInterface.updateFile(fileItem.id, updateData) - fileItem.workflowId = workflowId - # Convert FileItem to dictionary for JSON response fileMeta = fileItem.model_dump() + # If workflowId is provided, include it in the response (not stored in FileItem model) + if workflowId: + fileMeta["workflowId"] = workflowId + # Response with duplicate information return JSONResponse({ "message": message, diff --git a/modules/routes/routeDataUsers.py b/modules/routes/routeDataUsers.py index ed55b11f..b3da0c2e 100644 --- a/modules/routes/routeDataUsers.py +++ b/modules/routes/routeDataUsers.py @@ -21,7 +21,7 @@ import modules.interfaces.interfaceDbApp as interfaceDbApp from modules.auth import limiter, getRequestContext, RequestContext # Import the attribute definition and helper functions -from modules.datamodels.datamodelUam import User +from modules.datamodels.datamodelUam import User, UserInDB from modules.datamodels.datamodelPagination import PaginationParams, PaginatedResponse, PaginationMetadata, normalize_pagination_dict # Configure logger diff --git a/modules/routes/routeSecurityGoogle.py b/modules/routes/routeSecurityGoogle.py index 39edcf8b..a4795243 100644 --- a/modules/routes/routeSecurityGoogle.py +++ b/modules/routes/routeSecurityGoogle.py @@ -17,6 +17,7 @@ from modules.interfaces.interfaceDbApp import getInterface, getRootInterface from modules.datamodels.datamodelUam import AuthAuthority, User, ConnectionStatus, UserConnection from modules.auth import getCurrentUser, limiter from modules.auth import createAccessToken, setAccessTokenCookie, createRefreshToken, setRefreshTokenCookie +from modules.auth.tokenManager import TokenManager from modules.shared.timeUtils import createExpirationTimestamp, getUtcTimestamp, parseTimestamp # Configure logger diff --git a/modules/routes/routeSecurityMsft.py b/modules/routes/routeSecurityMsft.py index bc637222..921ddafe 100644 --- a/modules/routes/routeSecurityMsft.py +++ b/modules/routes/routeSecurityMsft.py @@ -18,6 +18,7 @@ from modules.datamodels.datamodelUam import AuthAuthority, User, ConnectionStatu from modules.datamodels.datamodelSecurity import Token from modules.auth import getCurrentUser, limiter from modules.auth import createAccessToken, setAccessTokenCookie, createRefreshToken, setRefreshTokenCookie +from modules.auth.tokenManager import TokenManager from modules.shared.timeUtils import createExpirationTimestamp, getUtcTimestamp, parseTimestamp # Configure logger diff --git a/modules/services/serviceAi/subContentExtraction.py b/modules/services/serviceAi/subContentExtraction.py index 696ba377..a7250a3a 100644 --- a/modules/services/serviceAi/subContentExtraction.py +++ b/modules/services/serviceAi/subContentExtraction.py @@ -15,7 +15,7 @@ import base64 from typing import Dict, Any, List, Optional from modules.datamodels.datamodelChat import ChatDocument -from modules.datamodels.datamodelExtraction import ContentPart, DocumentIntent +from modules.datamodels.datamodelExtraction import ContentPart, DocumentIntent, ExtractionOptions, MergeStrategy from modules.workflows.processing.shared.stateTools import checkWorkflowStopped logger = logging.getLogger(__name__) diff --git a/modules/workflows/methods/methodAi/actions/process.py b/modules/workflows/methods/methodAi/actions/process.py index cdaf55b6..1ab4a7ee 100644 --- a/modules/workflows/methods/methodAi/actions/process.py +++ b/modules/workflows/methods/methodAi/actions/process.py @@ -6,7 +6,7 @@ import time import json from typing import Dict, Any, List, Optional from modules.datamodels.datamodelChat import ActionResult, ActionDocument -from modules.datamodels.datamodelAi import AiCallOptions +from modules.datamodels.datamodelAi import AiCallOptions, OperationTypeEnum, ProcessingModeEnum from modules.datamodels.datamodelExtraction import ContentPart logger = logging.getLogger(__name__) diff --git a/modules/workflows/processing/core/taskPlanner.py b/modules/workflows/processing/core/taskPlanner.py index 94c695cb..b1e1def7 100644 --- a/modules/workflows/processing/core/taskPlanner.py +++ b/modules/workflows/processing/core/taskPlanner.py @@ -6,7 +6,7 @@ import json import logging from typing import Dict, Any -from modules.datamodels.datamodelChat import TaskStep, TaskContext, TaskPlan +from modules.datamodels.datamodelChat import TaskStep, TaskContext, TaskPlan, WorkflowModeEnum from modules.datamodels.datamodelAi import AiCallOptions, OperationTypeEnum, ProcessingModeEnum, PriorityEnum from modules.workflows.processing.shared.promptGenerationTaskplan import ( generateTaskPlanningPrompt diff --git a/modules/workflows/processing/modes/modeDynamic.py b/modules/workflows/processing/modes/modeDynamic.py index 45b92961..1510e512 100644 --- a/modules/workflows/processing/modes/modeDynamic.py +++ b/modules/workflows/processing/modes/modeDynamic.py @@ -11,7 +11,7 @@ from datetime import datetime, timezone from typing import List, Dict, Any from modules.datamodels.datamodelChat import ( TaskStep, TaskContext, TaskResult, ActionItem, TaskStatus, - ActionResult, Observation, ObservationPreview, ReviewResult + ActionResult, Observation, ObservationPreview, ReviewResult, ReviewContext ) from modules.datamodels.datamodelChat import ChatWorkflow from modules.workflows.processing.modes.modeBase import BaseMode diff --git a/modules/workflows/processing/workflowProcessor.py b/modules/workflows/processing/workflowProcessor.py index c73d280b..11879e9d 100644 --- a/modules/workflows/processing/workflowProcessor.py +++ b/modules/workflows/processing/workflowProcessor.py @@ -13,7 +13,7 @@ from modules.workflows.processing.modes.modeBase import BaseMode from modules.workflows.processing.modes.modeDynamic import DynamicMode from modules.workflows.processing.modes.modeAutomation import AutomationMode from modules.workflows.processing.shared.stateTools import checkWorkflowStopped -from modules.datamodels.datamodelAi import OperationTypeEnum, PriorityEnum, ProcessingModeEnum +from modules.datamodels.datamodelAi import OperationTypeEnum, PriorityEnum, ProcessingModeEnum, AiCallOptions from modules.shared.jsonUtils import extractJsonString, repairBrokenJson if TYPE_CHECKING: