# Copyright (c) 2025 Patrick Motsch # All rights reserved. """ Chat Playground routes for the backend API. Implements the endpoints for chat playground workflow management. """ import logging from typing import Optional, Dict, Any from fastapi import APIRouter, HTTPException, Depends, Body, Path, Query, Request # Import auth modules from modules.auth import limiter, getRequestContext, RequestContext # Import interfaces from . import interfaceFeatureAiChat as interfaceDbChat # Import models from .datamodelFeatureAiChat import ChatWorkflow, UserInputRequest, WorkflowModeEnum # Import workflow control functions from modules.workflows.automation import chatStart, chatStop # Configure logger logger = logging.getLogger(__name__) # Create router for chat playground endpoints router = APIRouter( prefix="/api/chat/playground", tags=["Chat Playground"], responses={404: {"description": "Not found"}} ) def _getServiceChat(context: RequestContext): return interfaceDbChat.getInterface(context.user, mandateId=str(context.mandateId) if context.mandateId else None) # Workflow start endpoint @router.post("/start", response_model=ChatWorkflow) @limiter.limit("120/minute") async def start_workflow( request: Request, workflowId: Optional[str] = Query(None, description="Optional ID of the workflow to continue"), workflowMode: WorkflowModeEnum = Query(..., description="Workflow mode: 'Dynamic' or 'Automation' (mandatory)"), userInput: UserInputRequest = Body(...), context: RequestContext = Depends(getRequestContext) ) -> ChatWorkflow: """ Starts a new workflow or continues an existing one. Corresponds to State 1 in the state machine documentation. Args: workflowMode: "Dynamic" for iterative dynamic-style processing, "Automation" for automated workflow execution """ try: # Start or continue workflow using playground controller workflow = await chatStart(context.user, userInput, workflowMode, workflowId) return workflow except Exception as e: logger.error(f"Error in start_workflow: {str(e)}") raise HTTPException( status_code=500, detail=str(e) ) # State 8: Workflow Stopped endpoint @router.post("/{workflowId}/stop", response_model=ChatWorkflow) @limiter.limit("120/minute") async def stop_workflow( request: Request, workflowId: str = Path(..., description="ID of the workflow to stop"), context: RequestContext = Depends(getRequestContext) ) -> ChatWorkflow: """Stops a running workflow.""" try: # Stop workflow using playground controller workflow = await chatStop(context.user, workflowId) return workflow except Exception as e: logger.error(f"Error in stop_workflow: {str(e)}") raise HTTPException( status_code=500, detail=str(e) ) # Unified Chat Data Endpoint for Polling @router.get("/{workflowId}/chatData") @limiter.limit("120/minute") async def get_workflow_chat_data( request: Request, workflowId: str = Path(..., description="ID of the workflow"), afterTimestamp: Optional[float] = Query(None, description="Unix timestamp to get data after"), context: RequestContext = Depends(getRequestContext) ) -> Dict[str, Any]: """ Get unified chat data (messages, logs, stats) for a workflow with timestamp-based selective data transfer. Returns all data types in chronological order based on _createdAt timestamp. """ try: # Get service center interfaceDbChat = _getServiceChat(context) # Verify workflow exists workflow = interfaceDbChat.getWorkflow(workflowId) if not workflow: raise HTTPException( status_code=404, detail=f"Workflow with ID {workflowId} not found" ) # Get unified chat data using the new method chatData = interfaceDbChat.getUnifiedChatData(workflowId, afterTimestamp) return chatData except HTTPException: raise except Exception as e: logger.error(f"Error getting unified chat data: {str(e)}", exc_info=True) raise HTTPException( status_code=500, detail=f"Error getting unified chat data: {str(e)}" )