feat: included service center in playground feature to replace old services
This commit is contained in:
parent
1e678a8897
commit
13322e7cf8
3 changed files with 142 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__)
|
||||
|
||||
|
|
@ -48,6 +48,16 @@ RESOURCE_OBJECTS = [
|
|||
},
|
||||
]
|
||||
|
||||
# Service requirements - services this feature needs from the service center
|
||||
# Same as automation: chatplayground runs the same WorkflowManager and workflow methods
|
||||
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.)"}},
|
||||
]
|
||||
# Template roles for this feature
|
||||
# Role names MUST follow convention: {featureCode}-{roleName}
|
||||
TEMPLATE_ROLES = [
|
||||
|
|
@ -104,6 +114,88 @@ TEMPLATE_ROLES = [
|
|||
]
|
||||
|
||||
|
||||
def getRequiredServiceKeys() -> List[str]:
|
||||
"""Return list of service keys this feature requires."""
|
||||
return [s["serviceKey"] for s in REQUIRED_SERVICES]
|
||||
|
||||
|
||||
def getChatplaygroundServices(
|
||||
user,
|
||||
mandateId: Optional[str] = None,
|
||||
featureInstanceId: Optional[str] = None,
|
||||
workflow=None,
|
||||
) -> "_ChatplaygroundServiceHub":
|
||||
"""
|
||||
Get a service hub for the chatplayground 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.
|
||||
"""
|
||||
from modules.serviceCenter import getService
|
||||
from modules.serviceCenter.context import ServiceCenterContext
|
||||
|
||||
_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 = _ChatplaygroundServiceHub()
|
||||
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 chatplayground: {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
|
||||
|
||||
return hub
|
||||
|
||||
|
||||
class _ChatplaygroundServiceHub:
|
||||
"""Lightweight hub exposing only services required by the chatplayground feature."""
|
||||
|
||||
user = None
|
||||
mandateId = None
|
||||
featureInstanceId = None
|
||||
workflow = None
|
||||
featureCode = "chatplayground"
|
||||
allowedProviders = None
|
||||
interfaceDbApp = None
|
||||
interfaceDbComponent = None
|
||||
interfaceDbChat = 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."""
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ from modules.datamodels.datamodelChat import ChatWorkflow, UserInputRequest, Wor
|
|||
|
||||
# Import workflow control functions
|
||||
from modules.workflows.automation import chatStart, chatStop
|
||||
from modules.features.chatplayground.mainChatplayground import getChatplaygroundServices
|
||||
|
||||
# Configure logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -95,6 +96,16 @@ async def start_workflow(
|
|||
# Validate access and get mandate ID
|
||||
mandateId = _validateInstanceAccess(instanceId, context)
|
||||
|
||||
# Get chatplayground services from service center (not automation)
|
||||
services = getChatplaygroundServices(
|
||||
context.user,
|
||||
mandateId=mandateId,
|
||||
featureInstanceId=instanceId,
|
||||
)
|
||||
services.featureCode = 'chatplayground'
|
||||
if hasattr(userInput, 'allowedProviders') and userInput.allowedProviders:
|
||||
services.allowedProviders = userInput.allowedProviders
|
||||
|
||||
# Start or continue workflow
|
||||
workflow = await chatStart(
|
||||
context.user,
|
||||
|
|
@ -103,7 +114,8 @@ async def start_workflow(
|
|||
workflowId,
|
||||
mandateId=mandateId,
|
||||
featureInstanceId=instanceId,
|
||||
featureCode='chatplayground'
|
||||
featureCode='chatplayground',
|
||||
services=services,
|
||||
)
|
||||
|
||||
return workflow
|
||||
|
|
@ -132,12 +144,22 @@ async def stop_workflow(
|
|||
# Validate access and get mandate ID
|
||||
mandateId = _validateInstanceAccess(instanceId, context)
|
||||
|
||||
# Get chatplayground services from service center (not automation)
|
||||
services = getChatplaygroundServices(
|
||||
context.user,
|
||||
mandateId=mandateId,
|
||||
featureInstanceId=instanceId,
|
||||
)
|
||||
services.featureCode = 'chatplayground'
|
||||
|
||||
# Stop workflow (pass featureInstanceId for proper RBAC filtering)
|
||||
workflow = await chatStop(
|
||||
context.user,
|
||||
workflowId,
|
||||
mandateId=mandateId,
|
||||
featureInstanceId=instanceId
|
||||
featureInstanceId=instanceId,
|
||||
featureCode='chatplayground',
|
||||
services=services,
|
||||
)
|
||||
|
||||
return workflow
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ from .subAutomationUtils import parseScheduleToCron, planToPrompt, replacePlaceh
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def chatStart(currentUser: User, userInput: UserInputRequest, workflowMode: WorkflowModeEnum, workflowId: Optional[str] = None, mandateId: Optional[str] = None, featureInstanceId: Optional[str] = None, featureCode: Optional[str] = None) -> ChatWorkflow:
|
||||
async def chatStart(currentUser: User, userInput: UserInputRequest, workflowMode: WorkflowModeEnum, workflowId: Optional[str] = None, mandateId: Optional[str] = None, featureInstanceId: Optional[str] = None, featureCode: Optional[str] = None, services=None) -> ChatWorkflow:
|
||||
"""
|
||||
Starts a new chat or continues an existing one, then launches processing asynchronously.
|
||||
|
||||
|
|
@ -36,9 +36,11 @@ async def chatStart(currentUser: User, userInput: UserInputRequest, workflowMode
|
|||
mandateId: Mandate ID (required for billing)
|
||||
featureInstanceId: Feature instance ID (required for billing)
|
||||
featureCode: Feature code (e.g., 'chatplayground', 'automation')
|
||||
services: Pre-built service hub from the calling feature (required). Each feature must pass its own services.
|
||||
"""
|
||||
if services is None:
|
||||
raise ValueError("services is required: each feature must pass its own service hub (e.g. getChatplaygroundServices, getAutomationServices)")
|
||||
try:
|
||||
services = getAutomationServices(currentUser, mandateId=mandateId, featureInstanceId=featureInstanceId)
|
||||
|
||||
# Store allowedProviders in services context for model selection
|
||||
if hasattr(userInput, 'allowedProviders') and userInput.allowedProviders:
|
||||
|
|
@ -56,10 +58,11 @@ async def chatStart(currentUser: User, userInput: UserInputRequest, workflowMode
|
|||
logger.error(f"Error starting chat: {str(e)}")
|
||||
raise
|
||||
|
||||
async def chatStop(currentUser: User, workflowId: str, mandateId: Optional[str] = None, featureInstanceId: Optional[str] = None, featureCode: Optional[str] = None) -> ChatWorkflow:
|
||||
"""Stops a running chat."""
|
||||
async def chatStop(currentUser: User, workflowId: str, mandateId: Optional[str] = None, featureInstanceId: Optional[str] = None, featureCode: Optional[str] = None, services=None) -> ChatWorkflow:
|
||||
"""Stops a running chat. Caller must pass services from the owning feature."""
|
||||
if services is None:
|
||||
raise ValueError("services is required: each feature must pass its own service hub (e.g. getChatplaygroundServices, getAutomationServices)")
|
||||
try:
|
||||
services = getAutomationServices(currentUser, mandateId=mandateId, featureInstanceId=featureInstanceId)
|
||||
if featureCode:
|
||||
services.featureCode = featureCode
|
||||
workflowManager = WorkflowManager(services)
|
||||
|
|
@ -146,6 +149,12 @@ async def executeAutomation(automationId: str, automation, creatorUser: User, se
|
|||
|
||||
# 3. Start workflow using chatStart with creator's context
|
||||
# mandateId and featureInstanceId come from the automation definition
|
||||
# Each feature must pass its own services - no fallback
|
||||
creatorServices = getAutomationServices(
|
||||
creatorUser,
|
||||
mandateId=automationMandateId,
|
||||
featureInstanceId=automationFeatureInstanceId,
|
||||
)
|
||||
workflow = await chatStart(
|
||||
currentUser=creatorUser,
|
||||
userInput=userInput,
|
||||
|
|
@ -153,7 +162,8 @@ async def executeAutomation(automationId: str, automation, creatorUser: User, se
|
|||
workflowId=None,
|
||||
mandateId=automationMandateId,
|
||||
featureInstanceId=automationFeatureInstanceId,
|
||||
featureCode='automation'
|
||||
featureCode='automation',
|
||||
services=creatorServices,
|
||||
)
|
||||
|
||||
executionLog["workflowId"] = workflow.id
|
||||
|
|
@ -161,9 +171,7 @@ async def executeAutomation(automationId: str, automation, creatorUser: User, se
|
|||
executionLog["messages"].append(f"Workflow {workflow.id} started successfully")
|
||||
logger.info(f"Started workflow {workflow.id} with plan containing {len(plan.get('tasks', []))} tasks (plan embedded in userInput)")
|
||||
|
||||
# Set workflow name with "automated" prefix — use creatorUser's Services
|
||||
# (services parameter is eventServices with eventUser context, must use creatorUser context)
|
||||
creatorServices = getAutomationServices(creatorUser, mandateId=automationMandateId, featureInstanceId=automationFeatureInstanceId)
|
||||
# Set workflow name with "automated" prefix — use creatorServices from chatStart
|
||||
automationLabel = automation.label or "Unknown Automation"
|
||||
workflowName = f"automated: {automationLabel}"
|
||||
creatorServices.interfaceDbChat.updateWorkflow(workflow.id, {"name": workflowName})
|
||||
|
|
|
|||
Loading…
Reference in a new issue