/* * Copyright 2025 Google. This software is provided as-is, without warranty or representation for any use or purpose. * Your use of it is subject to your agreement with Google. */ package com.example.mapper.rag; import com.example.dto.dialogflow.base.DetectIntentRequestDTO; import com.example.dto.dialogflow.conversation.QueryInputDTO; import com.example.dto.rag.RagQueryRequest; import com.example.dto.rag.RagQueryRequest.NotificationContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.Map; import java.util.Objects; /** * Mapper component responsible for converting DetectIntentRequestDTO to RAG API format. * This adapter preserves the existing DTO structure while translating to the simpler RAG API. */ @Component public class RagRequestMapper { private static final Logger logger = LoggerFactory.getLogger(RagRequestMapper.class); private static final String NOTIFICATION_PREFIX = "notification_po_"; private static final String NOTIFICATION_TEXT_PARAM = "notification_text"; /** * Maps a DetectIntentRequestDTO to a RagQueryRequest. * Extracts the phone number, text/event, and notification data from the existing structure. * * @param requestDto The existing DetectIntentRequestDTO * @param sessionId The session ID (not used by RAG but kept for logging) * @return A RagQueryRequest ready to send to the RAG server */ public RagQueryRequest mapToRagRequest(DetectIntentRequestDTO requestDto, String sessionId) { Objects.requireNonNull(requestDto, "DetectIntentRequestDTO cannot be null"); logger.debug("Mapping DetectIntentRequestDTO to RagQueryRequest for session: {}", sessionId); // Extract phone number from parameters Map parameters = requestDto.queryParams() != null ? requestDto.queryParams().parameters() : Map.of(); String phoneNumber = extractPhoneNumber(parameters); if (phoneNumber == null || phoneNumber.isBlank()) { logger.error("Phone number is required but not found in request parameters"); throw new IllegalArgumentException("Phone number is required in request parameters"); } // Extract text or event from QueryInputDTO QueryInputDTO queryInput = requestDto.queryInput(); String text = extractText(queryInput); String languageCode = queryInput.languageCode(); // Determine request type and notification context String type = determineRequestType(queryInput, parameters); NotificationContext notificationContext = extractNotificationContext(parameters); RagQueryRequest ragRequest = new RagQueryRequest( phoneNumber, text, type, notificationContext, languageCode ); logger.debug("Mapped RAG request: type={}, phoneNumber={}, hasNotification={}", type, phoneNumber, notificationContext != null); return ragRequest; } /** * Extracts the phone number from request parameters. */ private String extractPhoneNumber(Map parameters) { Object telefono = parameters.get("telefono"); if (telefono instanceof String) { return (String) telefono; } logger.warn("Phone number (telefono) not found or not a string in parameters"); return null; } /** * Extracts text from QueryInputDTO (either text input or event). * For events, we use the event name as the text. */ private String extractText(QueryInputDTO queryInput) { if (queryInput.text() != null && queryInput.text().text() != null && !queryInput.text().text().trim().isEmpty()) { return queryInput.text().text(); } else if (queryInput.event() != null && queryInput.event().event() != null && !queryInput.event().event().trim().isEmpty()) { // For events (like "LLM_RESPONSE_PROCESSED"), use the event name return queryInput.event().event(); } else { logger.error("Query input must contain either text or event"); throw new IllegalArgumentException("Query input must contain either text or event"); } } /** * Determines if this is a conversation or notification request. * If notification parameters are present, it's a notification request. */ private String determineRequestType(QueryInputDTO queryInput, Map parameters) { // Check if there are notification-prefixed parameters boolean hasNotificationParams = parameters.keySet().stream() .anyMatch(key -> key.startsWith(NOTIFICATION_PREFIX)); // Check if there's a notification_text parameter boolean hasNotificationText = parameters.containsKey(NOTIFICATION_TEXT_PARAM); // Check if the input is an event (notifications use events) boolean isEvent = queryInput.event() != null && queryInput.event().event() != null; if (hasNotificationParams || hasNotificationText || (isEvent && "notificacion".equals(queryInput.event().event()))) { return "notification"; } return "conversation"; } /** * Extracts notification context from parameters. * Looks for notification_text and notification_po_* parameters. */ private NotificationContext extractNotificationContext(Map parameters) { String notificationText = (String) parameters.get(NOTIFICATION_TEXT_PARAM); // Extract all notification_po_* parameters and remove the prefix Map notificationParams = new HashMap<>(); parameters.forEach((key, value) -> { if (key.startsWith(NOTIFICATION_PREFIX)) { String cleanKey = key.substring(NOTIFICATION_PREFIX.length()); notificationParams.put(cleanKey, value); } }); // Only create NotificationContext if we have notification data if (notificationText != null || !notificationParams.isEmpty()) { return new NotificationContext(notificationText, notificationParams); } return null; } }