# methodDiscovery.py # Method discovery and management for workflow execution import json import logging import importlib import pkgutil import inspect from typing import Any, Dict, List from modules.datamodels.datamodelChat import TaskContext, ReviewContext, DocumentExchange from modules.workflows.methods.methodBase import MethodBase # Set up logger logger = logging.getLogger(__name__) # Global methods catalog - moved from serviceCenter methods = {} def discoverMethods(serviceCenter): """Dynamically discover all method classes and their actions in modules methods package""" try: # Import the methods package methodsPackage = importlib.import_module('modules.workflows.methods') # Discover all modules in the package for _, name, isPkg in pkgutil.iter_modules(methodsPackage.__path__): if not isPkg and name.startswith('method'): try: # Import the module module = importlib.import_module(f'modules.workflows.methods.{name}') # Find all classes in the module that inherit from MethodBase for itemName, item in inspect.getmembers(module): if (inspect.isclass(item) and issubclass(item, MethodBase) and item != MethodBase): # Instantiate the method methodInstance = item(serviceCenter) # Use the actions property from MethodBase which handles @action decorator actions = methodInstance.actions # Create method info methodInfo = { 'instance': methodInstance, 'actions': actions, 'description': item.__doc__ or f"Method {itemName}" } # Store the method with full class name methods[itemName] = methodInfo # Also store with short name for action executor access shortName = itemName.replace('Method', '').lower() methods[shortName] = methodInfo logger.info(f"Discovered method {itemName} (short: {shortName}) with {len(actions)} actions") except Exception as e: logger.error(f"Error discovering method {name}: {str(e)}") continue logger.info(f"Discovered {len(methods)} method entries total") except Exception as e: logger.error(f"Error discovering methods: {str(e)}") def getMethodsList(serviceCenter): """Get a list of available methods with their signatures""" if not methods: discoverMethods(serviceCenter) methodsList = [] for methodName, methodInfo in methods.items(): methodDescription = methodInfo['description'] actionsList = [] for actionName, actionInfo in methodInfo['actions'].items(): actionDescription = actionInfo['description'] parameters = actionInfo['parameters'] # Build parameter signature paramSig = [] for paramName, paramInfo in parameters.items(): paramType = paramInfo['type'] paramRequired = paramInfo['required'] paramDefault = paramInfo['default'] if paramRequired: paramSig.append(f"{paramName}: {paramType}") else: defaultStr = f" = {paramDefault}" if paramDefault is not None else " = None" paramSig.append(f"{paramName}: {paramType}{defaultStr}") paramSignature = f"({', '.join(paramSig)})" if paramSig else "()" actionsList.append(f"- {actionName}{paramSignature}: {actionDescription}") actionsStr = "\n".join(actionsList) methodsList.append(f"**{methodName}**: {methodDescription}\n{actionsStr}") return "\n\n".join(methodsList) def getActionParameterSignature(methodName: str, actionName: str, methods: Dict[str, Any]) -> str: """Get action parameter signature from method docstring for AI parameter generation""" try: if not methods or methodName not in methods: return "" methodInstance = methods[methodName]['instance'] if actionName not in methodInstance.actions: return "" action_info = methodInstance.actions[actionName] # Extract parameter descriptions from docstring docstring = action_info.get('description', '') paramDescriptions, paramTypes = methodInstance._extractParameterDetails(docstring) param_list = [] for paramName, paramDesc in paramDescriptions.items(): paramType = paramTypes.get(paramName, 'Any') if paramDesc: param_list.append(f"- {paramName} ({paramType}): {paramDesc}") else: param_list.append(f"- {paramName} ({paramType})") return "Required parameters:\n" + "\n".join(param_list) except Exception as e: logger.error(f"Error getting action parameter signature for {methodName}.{actionName}: {str(e)}") return ""