from enum import Enum from typing import Dict, List, Optional, Any, Literal from datetime import datetime, UTC from pydantic import BaseModel, Field import logging from modules.interfaces.interfaceChatModel import ActionResult from functools import wraps from inspect import signature logger = logging.getLogger(__name__) def action(func): """Decorator to mark a method as an available action""" @wraps(func) async def wrapper(self, parameters: Dict[str, Any], *args, **kwargs): return await func(self, parameters, *args, **kwargs) wrapper.is_action = True return wrapper class MethodBase: """Base class for all methods""" def __init__(self, serviceContainer: Any): """Initialize method with service container""" self.service = serviceContainer self.name: str self.description: str self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}") @property def actions(self) -> Dict[str, Dict[str, Any]]: """Dynamically collect all actions decorated with @action in the class.""" actions = {} for attr_name in dir(self): # Skip the actions property itself to avoid recursion if attr_name == 'actions': continue try: attr = getattr(self, attr_name) if callable(attr) and getattr(attr, 'is_action', False): sig = signature(attr) params = {} for param_name, param in sig.parameters.items(): if param_name not in ['self', 'parameters', 'authData']: param_type = param.annotation if param.annotation != param.empty else Any params[param_name] = { 'type': param_type, 'required': param.default == param.empty, 'description': None, 'default': param.default if param.default != param.empty else None } actions[attr_name] = { 'description': attr.__doc__ or '', 'parameters': params, 'method': attr } except (AttributeError, RecursionError): # Skip attributes that cause issues continue return actions def getActionSignature(self, actionName: str) -> str: """Get formatted action signature for AI prompt generation""" if actionName not in self.actions: return "" action = self.actions[actionName] paramList = [] for paramName, param in action['parameters'].items(): paramType = self._formatType(param['type']) paramList.append(f"{paramName}:{paramType}") signature = f"{self.name}.{actionName}([{', '.join(paramList)}])" if action.get('description'): signature += f" # {action['description']}" return signature def _formatType(self, type_annotation) -> str: """Format type annotation for display""" if type_annotation == Any: return "Any" elif hasattr(type_annotation, '__name__'): return type_annotation.__name__ elif hasattr(type_annotation, '_name'): return type_annotation._name else: return str(type_annotation) async def execute(self, action: str, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> ActionResult: """ Execute method action with authentication data Args: action: The action to execute parameters: Action parameters authData: Authentication data Returns: ActionResult containing execution results Raises: ValueError: If action is not supported RuntimeError: If authentication fails """ try: # Validate action if action not in self.actions: raise ValueError(f"Unsupported action: {action}") # Validate parameters if not await self.validateParameters(action, parameters): return self._createResult( success=False, data={}, error="Invalid parameters" ) # Validate authentication if not self._validateAuth(authData): return self._createResult( success=False, data={}, error="Authentication failed" ) # Execute action return await self._executeAction(action, parameters, authData) except Exception as e: self.logger.error(f"Error executing action {action}: {str(e)}") return self._createResult( success=False, data={}, error=str(e) ) async def _executeAction(self, action: str, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> ActionResult: """Execute specific action - to be implemented by subclasses""" raise NotImplementedError async def validateParameters(self, action: str, parameters: Dict[str, Any]) -> bool: """Validate action parameters""" try: if action not in self.actions: return False actionDef = self.actions[action] requiredParams = {k for k, v in actionDef['parameters'].items() if v['required']} return all(param in parameters for param in requiredParams) except Exception as e: self.logger.error(f"Error validating parameters: {str(e)}") return False async def rollback(self, action: str, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> None: """Rollback action if needed""" try: await self._rollbackAction(action, parameters, authData) except Exception as e: self.logger.error(f"Error rolling back action {action}: {str(e)}") raise async def _rollbackAction(self, action: str, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> None: """Rollback specific action - to be implemented by subclasses""" pass def _createResult(self, success: bool, data: Dict[str, Any], metadata: Optional[Dict[str, Any]] = None, error: Optional[str] = None) -> ActionResult: """Create a method result""" return ActionResult( success=success, data=data, metadata=metadata or {}, validation=[], error=error ) def _addValidationMessage(self, result: ActionResult, message: str) -> None: """Add a validation message to the result""" result.validation.append(message)