Merge branch 'feat/service-center' into int
This commit is contained in:
commit
f4fb4637ea
5 changed files with 126 additions and 20 deletions
|
|
@ -6,7 +6,7 @@ Handles feature initialization and RBAC catalog registration.
|
|||
"""
|
||||
|
||||
import logging
|
||||
from typing import Dict, List, Any
|
||||
from typing import Dict, List, Any, Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -121,6 +121,106 @@ TEMPLATE_ROLES = [
|
|||
},
|
||||
]
|
||||
|
||||
# Service requirements - services this feature needs from the service center
|
||||
REQUIRED_SERVICES = [
|
||||
{"serviceKey": "chat", "meta": {"usage": "Workflow CRUD, messages, logs"}},
|
||||
{"serviceKey": "ai", "meta": {"usage": "AI planning for workflow execution"}},
|
||||
{"serviceKey": "utils", "meta": {"usage": "Timestamps, utilities"}},
|
||||
{"serviceKey": "billing", "meta": {"usage": "AI call billing"}},
|
||||
{"serviceKey": "extraction", "meta": {"usage": "Workflow method actions"}},
|
||||
{"serviceKey": "sharepoint", "meta": {"usage": "SharePoint actions (listDocuments, uploadDocument, etc.)"}},
|
||||
]
|
||||
|
||||
|
||||
def getRequiredServiceKeys() -> List[str]:
|
||||
"""Return list of service keys this feature requires."""
|
||||
return [s["serviceKey"] for s in REQUIRED_SERVICES]
|
||||
|
||||
|
||||
def getAutomationServices(
|
||||
user,
|
||||
mandateId: Optional[str] = None,
|
||||
featureInstanceId: Optional[str] = None,
|
||||
workflow=None,
|
||||
) -> "_AutomationServiceHub":
|
||||
"""
|
||||
Get a service hub for the automation feature using the service center.
|
||||
Resolves only the services declared in REQUIRED_SERVICES.
|
||||
No legacy fallback - service center only.
|
||||
|
||||
Returns a hub-like object with: chat, ai, utils, billing, extraction,
|
||||
sharepoint, rbac, interfaceDbApp, interfaceDbComponent, interfaceDbChat,
|
||||
interfaceDbAutomation.
|
||||
"""
|
||||
from modules.serviceCenter import getService
|
||||
from modules.serviceCenter.context import ServiceCenterContext
|
||||
from modules.features.automation.interfaceFeatureAutomation import getInterface as getAutomationInterface
|
||||
|
||||
_workflow = workflow
|
||||
if _workflow is None:
|
||||
_workflow = type("_Placeholder", (), {"featureCode": FEATURE_CODE})()
|
||||
ctx = ServiceCenterContext(
|
||||
user=user,
|
||||
mandate_id=mandateId,
|
||||
feature_instance_id=featureInstanceId,
|
||||
workflow=_workflow,
|
||||
)
|
||||
|
||||
hub = _AutomationServiceHub()
|
||||
hub.user = user
|
||||
hub.mandateId = mandateId
|
||||
hub.featureInstanceId = featureInstanceId
|
||||
hub.workflow = workflow
|
||||
hub.featureCode = FEATURE_CODE
|
||||
hub.allowedProviders = None
|
||||
|
||||
for spec in REQUIRED_SERVICES:
|
||||
key = spec["serviceKey"]
|
||||
try:
|
||||
svc = getService(key, ctx, legacy_hub=None)
|
||||
setattr(hub, key, svc)
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not resolve service '{key}' for automation: {e}")
|
||||
setattr(hub, key, None)
|
||||
|
||||
# Copy interfaces from chat service for WorkflowManager compatibility
|
||||
if hub.chat:
|
||||
hub.interfaceDbApp = getattr(hub.chat, "interfaceDbApp", None)
|
||||
hub.interfaceDbComponent = getattr(hub.chat, "interfaceDbComponent", None)
|
||||
hub.interfaceDbChat = getattr(hub.chat, "interfaceDbChat", None)
|
||||
|
||||
# RBAC for MethodBase action permission checks (workflow methods)
|
||||
hub.rbac = getattr(hub.interfaceDbApp, "rbac", None) if hub.interfaceDbApp else None
|
||||
|
||||
# Set interfaceDbAutomation from feature interface
|
||||
hub.interfaceDbAutomation = getAutomationInterface(
|
||||
user, mandateId=mandateId, featureInstanceId=featureInstanceId
|
||||
)
|
||||
|
||||
return hub
|
||||
|
||||
|
||||
class _AutomationServiceHub:
|
||||
"""Lightweight hub exposing only services required by the automation feature."""
|
||||
|
||||
user = None
|
||||
mandateId = None
|
||||
featureInstanceId = None
|
||||
workflow = None
|
||||
featureCode = "automation"
|
||||
allowedProviders = None
|
||||
interfaceDbApp = None
|
||||
interfaceDbComponent = None
|
||||
interfaceDbChat = None
|
||||
interfaceDbAutomation = None
|
||||
rbac = None
|
||||
chat = None
|
||||
ai = None
|
||||
utils = None
|
||||
billing = None
|
||||
extraction = None
|
||||
sharepoint = None
|
||||
|
||||
|
||||
def getFeatureDefinition() -> Dict[str, Any]:
|
||||
"""Return the feature definition for registration."""
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import json
|
|||
|
||||
# Import interfaces and models
|
||||
from modules.features.automation.interfaceFeatureAutomation import getInterface as getAutomationInterface
|
||||
from modules.features.automation.mainAutomation import getAutomationServices
|
||||
from modules.auth import limiter, getRequestContext, RequestContext
|
||||
from modules.features.automation.datamodelFeatureAutomation import AutomationDefinition, AutomationTemplate
|
||||
from modules.datamodels.datamodelChat import ChatWorkflow
|
||||
|
|
@ -147,12 +148,14 @@ def get_available_actions(
|
|||
"""
|
||||
try:
|
||||
from modules.workflows.processing.shared.methodDiscovery import methods, discoverMethods
|
||||
from modules.services import getInterface as getServices
|
||||
|
||||
# Ensure methods are discovered (need a service center for discovery)
|
||||
# Ensure methods are discovered (need a service hub for discovery)
|
||||
if not methods:
|
||||
# Create a lightweight service center for method discovery
|
||||
services = getServices(context.user, mandateId=context.mandateId)
|
||||
services = getAutomationServices(
|
||||
context.user,
|
||||
mandateId=context.mandateId,
|
||||
featureInstanceId=context.featureInstanceId,
|
||||
)
|
||||
discoverMethods(services)
|
||||
|
||||
actionsList = []
|
||||
|
|
@ -365,8 +368,11 @@ async def execute_automation_route(
|
|||
) -> ChatWorkflow:
|
||||
"""Execute an automation immediately (test mode)"""
|
||||
try:
|
||||
from modules.services import getInterface as getServices
|
||||
services = getServices(context.user, mandateId=context.mandateId, featureInstanceId=context.featureInstanceId)
|
||||
services = getAutomationServices(
|
||||
context.user,
|
||||
mandateId=context.mandateId,
|
||||
featureInstanceId=context.featureInstanceId,
|
||||
)
|
||||
|
||||
# Load automation with current user's context (user has RBAC permissions via UI)
|
||||
automation = services.interfaceDbAutomation.getAutomationDefinition(automationId, includeSystemFields=True)
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ def get_all_automation_events(
|
|||
try:
|
||||
from modules.shared.eventManagement import eventManager
|
||||
from modules.interfaces.interfaceDbApp import getRootInterface
|
||||
from modules.services import getInterface as getServices
|
||||
from modules.features.automation.mainAutomation import getAutomationServices
|
||||
|
||||
if not eventManager.scheduler:
|
||||
return []
|
||||
|
|
@ -74,7 +74,7 @@ def get_all_automation_events(
|
|||
rootInterface = getRootInterface()
|
||||
eventUser = rootInterface.getUserByUsername("event")
|
||||
if eventUser:
|
||||
services = getServices(currentUser, None)
|
||||
services = getAutomationServices(currentUser, mandateId=None, featureInstanceId=None)
|
||||
allAutomations = services.interfaceDbAutomation.getAllAutomationDefinitionsWithRBAC(eventUser)
|
||||
|
||||
# Build lookup by automation ID
|
||||
|
|
@ -171,8 +171,8 @@ async def sync_all_automation_events(
|
|||
detail="Event user not available"
|
||||
)
|
||||
|
||||
from modules.services import getInterface as getServices
|
||||
services = getServices(currentUser, None)
|
||||
from modules.features.automation.mainAutomation import getAutomationServices
|
||||
services = getAutomationServices(currentUser, mandateId=None, featureInstanceId=None)
|
||||
result = syncAutomationEvents(services, eventUser)
|
||||
return {
|
||||
"success": True,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ from modules.features.automation.datamodelFeatureAutomation import AutomationDef
|
|||
from modules.datamodels.datamodelUam import User
|
||||
from modules.shared.timeUtils import getUtcTimestamp
|
||||
from modules.shared.eventManagement import eventManager
|
||||
from modules.services import getInterface as getServices
|
||||
from modules.features.automation.mainAutomation import getAutomationServices
|
||||
from modules.workflows.workflowManager import WorkflowManager
|
||||
from .subAutomationUtils import parseScheduleToCron, planToPrompt, replacePlaceholders
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ async def chatStart(currentUser: User, userInput: UserInputRequest, workflowMode
|
|||
featureCode: Feature code (e.g., 'chatplayground', 'automation')
|
||||
"""
|
||||
try:
|
||||
services = getServices(currentUser, mandateId=mandateId, featureInstanceId=featureInstanceId)
|
||||
services = getAutomationServices(currentUser, mandateId=mandateId, featureInstanceId=featureInstanceId)
|
||||
|
||||
# Store allowedProviders in services context for model selection
|
||||
if hasattr(userInput, 'allowedProviders') and userInput.allowedProviders:
|
||||
|
|
@ -59,7 +59,7 @@ async def chatStart(currentUser: User, userInput: UserInputRequest, workflowMode
|
|||
async def chatStop(currentUser: User, workflowId: str, mandateId: Optional[str] = None, featureInstanceId: Optional[str] = None, featureCode: Optional[str] = None) -> ChatWorkflow:
|
||||
"""Stops a running chat."""
|
||||
try:
|
||||
services = getServices(currentUser, mandateId=mandateId, featureInstanceId=featureInstanceId)
|
||||
services = getAutomationServices(currentUser, mandateId=mandateId, featureInstanceId=featureInstanceId)
|
||||
if featureCode:
|
||||
services.featureCode = featureCode
|
||||
workflowManager = WorkflowManager(services)
|
||||
|
|
@ -163,7 +163,7 @@ async def executeAutomation(automationId: str, automation, creatorUser: User, se
|
|||
|
||||
# Set workflow name with "automated" prefix — use creatorUser's Services
|
||||
# (services parameter is eventServices with eventUser context, must use creatorUser context)
|
||||
creatorServices = getServices(creatorUser, mandateId=automationMandateId, featureInstanceId=automationFeatureInstanceId)
|
||||
creatorServices = getAutomationServices(creatorUser, mandateId=automationMandateId, featureInstanceId=automationFeatureInstanceId)
|
||||
automationLabel = automation.label or "Unknown Automation"
|
||||
workflowName = f"automated: {automationLabel}"
|
||||
creatorServices.interfaceDbChat.updateWorkflow(workflow.id, {"name": workflowName})
|
||||
|
|
@ -292,7 +292,7 @@ def createAutomationEventHandler(automationId: str, eventUser):
|
|||
return
|
||||
|
||||
# Load automation using SysAdmin eventUser (has unrestricted access)
|
||||
eventServices = getServices(eventUser, None)
|
||||
eventServices = getAutomationServices(eventUser, mandateId=None, featureInstanceId=None)
|
||||
automation = eventServices.interfaceDbAutomation.getAutomationDefinition(automationId, includeSystemFields=True)
|
||||
if not automation or not getattr(automation, "active", False):
|
||||
logger.warning(f"Automation {automationId} not found or not active, skipping execution")
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ the automation scheduler (loading/syncing scheduled automation events).
|
|||
"""
|
||||
|
||||
import logging
|
||||
from modules.services import getInterface as getServices
|
||||
from modules.features.automation.mainAutomation import getAutomationServices
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -31,12 +31,12 @@ def start(eventUser) -> bool:
|
|||
from modules.shared.callbackRegistry import callbackRegistry
|
||||
|
||||
# Get services for event user (provides access to interfaces)
|
||||
services = getServices(eventUser, None)
|
||||
services = getAutomationServices(eventUser, mandateId=None, featureInstanceId=None)
|
||||
|
||||
# Register callback for automation changes
|
||||
def onAutomationChanged(chatInterface):
|
||||
"""Callback triggered when automations are created/updated/deleted."""
|
||||
eventServices = getServices(eventUser, None)
|
||||
eventServices = getAutomationServices(eventUser, mandateId=None, featureInstanceId=None)
|
||||
syncAutomationEvents(eventServices, eventUser)
|
||||
|
||||
callbackRegistry.register('automation.changed', onAutomationChanged)
|
||||
|
|
|
|||
Loading…
Reference in a new issue