""" Router for the chat-to-tasks functionality. """ import logging from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from app.api import deps from app.crud import task as task_crud from app.models.user import User from app.schemas.chat import ChatInput, ChatResponse, ChatProcessingError from app.schemas.task import TaskCreate, TaskRead from app.services.llm_service import LLMService, get_llm_service from app.db.session import get_db logger = logging.getLogger(__name__) router = APIRouter() @router.post("/chat-to-tasks", response_model=ChatResponse) async def create_tasks_from_chat( chat_input: ChatInput, db: Session = Depends(get_db), current_user: User = Depends(deps.get_current_active_user), llm_service: LLMService = Depends(get_llm_service), ): """ Convert natural language chat input into one or more structured task objects. Steps: 1. Validate the input message length. 2. Send the message to the LLM service for task extraction. 3. Parse the response into TaskCreate schemas. 4. Persist tasks in the database linked to the authenticated user. 5. Return the list of created tasks or an error response. """ message = chat_input.message.strip() if len(message) < 3: raise HTTPException( status_code=400, detail="Message must be at least 3 characters long", ) response = ChatResponse(original_message=message) try: logger.info(f"Received chat input for task extraction: {message[:50]}...") # Extract tasks from the LLM service llm_tasks = await llm_service.chat_to_tasks(message) if not llm_tasks: logger.warning("LLM service returned no tasks.") response.processing_successful = False response.error = ChatProcessingError( error_type="parsing_error", error_detail="No tasks could be extracted from your message.", ) return response created_tasks = [] for task_data in llm_tasks: try: # Map LLM response fields to TaskCreate schema fields task_create_data = { "title": task_data.get("title", "Untitled Task"), "description": task_data.get("description", ""), "priority": task_data.get("priority", "medium").lower(), } # Validate and include due_date if present and valid due_date = task_data.get("due_date") if due_date and due_date != "null": task_create_data["due_date"] = due_date # Map status field and normalize to internal status naming status = task_data.get("status", "").lower() if status == "pending": task_create_data["status"] = "todo" elif status: task_create_data["status"] = status # Validate input data against TaskCreate schema task_in = TaskCreate(**task_create_data) # Create task with ownership linked to current user db_task = task_crud.task.create_with_owner( db=db, obj_in=task_in, user_id=current_user.id, ) created_tasks.append(TaskRead.model_validate(db_task)) except Exception as task_exc: logger.error(f"Failed to create task from LLM data: {task_exc}") # Continue processing remaining tasks even if one fails if not created_tasks: response.processing_successful = False response.error = ChatProcessingError( error_type="creation_error", error_detail="Failed to create any tasks from the provided message.", ) else: response.tasks = created_tasks return response except Exception as exc: logger.exception(f"Unexpected error in chat-to-tasks endpoint: {exc}") response.processing_successful = False response.error = ChatProcessingError( error_type="processing_error", error_detail=f"An error occurred while processing your request: {str(exc)}", ) return response