105 lines
4.3 KiB
Python
105 lines
4.3 KiB
Python
# Copyright (c) 2025 Patrick Motsch
|
|
# All rights reserved.
|
|
# methodDiscovery.py
|
|
# Method discovery and management for workflow execution
|
|
|
|
import logging
|
|
import importlib
|
|
import pkgutil
|
|
import inspect
|
|
from typing import Any, Dict, List
|
|
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.
|
|
|
|
Always creates fresh method instances bound to the given serviceCenter,
|
|
preventing stale or cross-workflow service references.
|
|
"""
|
|
global methods
|
|
try:
|
|
methodsPackage = importlib.import_module('modules.workflows.methods')
|
|
|
|
# Clear and rebuild to prevent cross-workflow state contamination
|
|
methods.clear()
|
|
uniqueCount = 0
|
|
|
|
for _, name, isPkg in pkgutil.iter_modules(methodsPackage.__path__):
|
|
if name.startswith('method'):
|
|
try:
|
|
module = importlib.import_module(f'modules.workflows.methods.{name}')
|
|
|
|
for itemName, item in inspect.getmembers(module):
|
|
if (inspect.isclass(item) and
|
|
issubclass(item, MethodBase) and
|
|
item != MethodBase):
|
|
|
|
shortName = itemName.replace('Method', '').lower()
|
|
|
|
# Skip if already processed (via another module path)
|
|
if itemName in methods:
|
|
continue
|
|
|
|
methodInstance = item(serviceCenter)
|
|
actions = methodInstance.actions
|
|
|
|
methodInfo = {
|
|
'instance': methodInstance,
|
|
'actions': actions,
|
|
'description': item.__doc__ or f"Method {itemName}"
|
|
}
|
|
|
|
methods[itemName] = methodInfo
|
|
methods[shortName] = methodInfo
|
|
uniqueCount += 1
|
|
|
|
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 {uniqueCount} unique methods ({len(methods)} entries with aliases)")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error discovering methods: {str(e)}")
|
|
|
|
def getActionParameterList(methodName: str, actionName: str, methods: Dict[str, Any]) -> str:
|
|
"""Get action parameter list from WorkflowActionParameter structure for AI parameter generation (list only)."""
|
|
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]
|
|
# Use structured WorkflowActionParameter objects from new system
|
|
parameters = action_info.get('parameters', {})
|
|
|
|
param_list = []
|
|
for paramName, paramInfo in parameters.items():
|
|
paramType = paramInfo.get('type', 'Any')
|
|
paramDesc = paramInfo.get('description', '')
|
|
paramRequired = paramInfo.get('required', False)
|
|
|
|
# Format: paramName (type, required/optional): description
|
|
reqText = "required" if paramRequired else "optional"
|
|
if paramDesc:
|
|
param_list.append(f"- {paramName} ({paramType}, {reqText}): {paramDesc}")
|
|
else:
|
|
param_list.append(f"- {paramName} ({paramType}, {reqText})")
|
|
|
|
# Return list only, without leading headings or trailing text
|
|
return "\n".join(param_list)
|
|
except Exception as e:
|
|
logger.error(f"Error getting action parameter signature for {methodName}.{actionName}: {str(e)}")
|
|
return ""
|
|
|