diff --git a/pom.xml b/pom.xml
index 35267b4..b1f49d6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -91,7 +91,7 @@
com.google.genai
google-genai
- 1.8.0
+ 1.13.0
com.google.protobuf
diff --git a/src/main/java/com/example/dto/dialogflow/notification/ExternalNotRequestDTO.java b/src/main/java/com/example/dto/dialogflow/notification/ExternalNotRequestDTO.java
index 3113deb..ae1cae7 100644
--- a/src/main/java/com/example/dto/dialogflow/notification/ExternalNotRequestDTO.java
+++ b/src/main/java/com/example/dto/dialogflow/notification/ExternalNotRequestDTO.java
@@ -9,8 +9,10 @@ import com.fasterxml.jackson.annotation.JsonProperty;
@JsonIgnoreProperties(ignoreUnknown = true)
public record ExternalNotRequestDTO(
- @JsonProperty("texto") String text,
- @JsonProperty("telefono") String phoneNumber) {
+ @JsonProperty("texto") String text,
+ @JsonProperty("telefono") String phoneNumber,
+ @JsonProperty("parametrosOcultos") java.util.Map hiddenParameters
+) {
public ExternalNotRequestDTO {
}
}
\ No newline at end of file
diff --git a/src/main/java/com/example/dto/quickreplies/QuickReplyDTO.java b/src/main/java/com/example/dto/quickreplies/QuickReplyDTO.java
index 9f690ef..cf1b97c 100644
--- a/src/main/java/com/example/dto/quickreplies/QuickReplyDTO.java
+++ b/src/main/java/com/example/dto/quickreplies/QuickReplyDTO.java
@@ -3,5 +3,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
public record QuickReplyDTO(
@JsonProperty("header") String header,
+ @JsonProperty("body") String body,
+ @JsonProperty("button") String button,
+ @JsonProperty("header_section") String headerSection,
@JsonProperty("preguntas") List preguntas
) {}
\ No newline at end of file
diff --git a/src/main/java/com/example/exception/DialogflowClientException.java b/src/main/java/com/example/exception/DialogflowClientException.java
index a30a665..47badd5 100644
--- a/src/main/java/com/example/exception/DialogflowClientException.java
+++ b/src/main/java/com/example/exception/DialogflowClientException.java
@@ -1,3 +1,8 @@
+/*
+ * 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.exception;
public class DialogflowClientException extends RuntimeException {
diff --git a/src/main/java/com/example/exception/FirestorePersistenceException.java b/src/main/java/com/example/exception/FirestorePersistenceException.java
index 28a3d17..b53abcc 100644
--- a/src/main/java/com/example/exception/FirestorePersistenceException.java
+++ b/src/main/java/com/example/exception/FirestorePersistenceException.java
@@ -1,3 +1,8 @@
+/*
+ * 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.exception;
public class FirestorePersistenceException extends RuntimeException {
diff --git a/src/main/java/com/example/exception/GeminiClientException.java b/src/main/java/com/example/exception/GeminiClientException.java
index 6e76ee2..fed2b7b 100644
--- a/src/main/java/com/example/exception/GeminiClientException.java
+++ b/src/main/java/com/example/exception/GeminiClientException.java
@@ -1,3 +1,8 @@
+/*
+ * 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.exception;
public class GeminiClientException extends Exception {
diff --git a/src/main/java/com/example/service/conversation/ConversationManagerService.java b/src/main/java/com/example/service/conversation/ConversationManagerService.java
index a563e8b..91e492a 100644
--- a/src/main/java/com/example/service/conversation/ConversationManagerService.java
+++ b/src/main/java/com/example/service/conversation/ConversationManagerService.java
@@ -47,7 +47,6 @@ public class ConversationManagerService {
private final ConversationContextMapper conversationContextMapper;
private final DataLossPrevention dataLossPrevention;
private final String dlpTemplateCompleteFlow;
- private final String dlpTemplatePersistFlow;
public ConversationManagerService(
DialogflowClientService dialogflowServiceClient,
@@ -60,8 +59,7 @@ public class ConversationManagerService {
NotificationContextMapper notificationContextMapper,
ConversationContextMapper conversationContextMapper,
DataLossPrevention dataLossPrevention,
- @Value("${google.cloud.dlp.dlpTemplateCompleteFlow}") String dlpTemplateCompleteFlow,
- @Value("${google.cloud.dlp.dlpTemplatePersistFlow}") String dlpTemplatePersistFlow) {
+ @Value("${google.cloud.dlp.dlpTemplateCompleteFlow}") String dlpTemplateCompleteFlow) {
this.dialogflowServiceClient = dialogflowServiceClient;
this.firestoreConversationService = firestoreConversationService;
this.memoryStoreConversationService = memoryStoreConversationService;
@@ -73,7 +71,6 @@ public class ConversationManagerService {
this.conversationContextMapper = conversationContextMapper;
this.dataLossPrevention = dataLossPrevention;
this.dlpTemplateCompleteFlow = dlpTemplateCompleteFlow;
- this.dlpTemplatePersistFlow = dlpTemplatePersistFlow;
}
public Mono manageConversation(ExternalConvRequestDTO externalrequest) {
return dataLossPrevention.getObfuscatedString(externalrequest.message(), dlpTemplateCompleteFlow)
@@ -193,10 +190,7 @@ public class ConversationManagerService {
private Mono processDialogflowRequest(ConversationSessionDTO session, DetectIntentRequestDTO request, String userId, String userMessageText, String userPhoneNumber, boolean newSession) {
final String finalSessionId = session.sessionId();
- return dataLossPrevention.getObfuscatedString(userMessageText, dlpTemplatePersistFlow)
- .flatMap(obfuscatedUserMessageText -> {
-
- ConversationEntryDTO userEntry = ConversationEntryDTO.forUser(obfuscatedUserMessageText);
+ ConversationEntryDTO userEntry = ConversationEntryDTO.forUser(userMessageText);
return this.persistConversationTurn(userId, finalSessionId, userEntry, userPhoneNumber)
.doOnSuccess(v -> logger.debug("User entry successfully persisted for session {}. Proceeding to Dialogflow...", finalSessionId))
@@ -210,7 +204,6 @@ public class ConversationManagerService {
})
.doOnError(error -> logger.error("Overall error during conversation management for session {}: {}", finalSessionId, error.getMessage(), error))
));
- });
}
private Mono startNotificationConversation(ConversationContext context, DetectIntentRequestDTO request, NotificationDTO notification) {
final String userId = context.userId();
diff --git a/src/main/java/com/example/service/notification/MemoryStoreNotificationService.java b/src/main/java/com/example/service/notification/MemoryStoreNotificationService.java
index 611412b..e90b0b7 100644
--- a/src/main/java/com/example/service/notification/MemoryStoreNotificationService.java
+++ b/src/main/java/com/example/service/notification/MemoryStoreNotificationService.java
@@ -1,3 +1,8 @@
+/*
+ * 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.service.notification;
import com.example.dto.dialogflow.conversation.ConversationEntryDTO;
diff --git a/src/main/java/com/example/service/notification/NotificationManagerService.java b/src/main/java/com/example/service/notification/NotificationManagerService.java
index 92ddf31..c284a2c 100644
--- a/src/main/java/com/example/service/notification/NotificationManagerService.java
+++ b/src/main/java/com/example/service/notification/NotificationManagerService.java
@@ -1,3 +1,8 @@
+/*
+ * 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.service.notification;
import com.example.dto.dialogflow.notification.ExternalNotRequestDTO;
@@ -103,7 +108,11 @@ public Mono processNotification(ExternalNotRequestDTO e
parameters.put("telefono", telefono);
parameters.put(NOTIFICATION_TEXT_PARAM, newNotificationEntry.texto());
- // Use a TextInputDTO to correctly build the QueryInputDTO
+ if (externalRequest.hiddenParameters() != null && !externalRequest.hiddenParameters().isEmpty()) {
+ parameters.putAll(externalRequest.hiddenParameters());
+ }
+
+ // Use a TextInputDTO to correctly build the QueryInputDTO
TextInputDTO textInput = new TextInputDTO(newNotificationEntry.texto());
QueryInputDTO queryInput = new QueryInputDTO(textInput, null, defaultLanguageCode);
diff --git a/src/main/java/com/example/service/quickreplies/QuickRepliesManagerService.java b/src/main/java/com/example/service/quickreplies/QuickRepliesManagerService.java
index adb42b8..db7b773 100644
--- a/src/main/java/com/example/service/quickreplies/QuickRepliesManagerService.java
+++ b/src/main/java/com/example/service/quickreplies/QuickRepliesManagerService.java
@@ -11,7 +11,6 @@ import com.example.dto.dialogflow.conversation.ConversationEntryEntity;
import com.example.dto.dialogflow.conversation.ConversationEntryType;
import com.example.dto.dialogflow.conversation.ExternalConvRequestDTO;
import com.example.dto.quickreplies.QuickReplyScreenRequestDTO;
-import com.example.service.conversation.DataLossPrevention;
import com.example.dto.quickreplies.QuestionDTO;
import com.example.dto.quickreplies.QuickReplyDTO;
import com.example.service.conversation.FirestoreConversationService;
@@ -20,10 +19,14 @@ import com.example.util.SessionIdGenerator;
import java.time.Instant;
import java.util.Collections;
import java.util.List;
+import java.util.stream.IntStream;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
+import com.example.dto.dialogflow.conversation.QueryResultDTO;
+import com.example.service.conversation.ConversationManagerService;
+import org.springframework.context.annotation.Lazy;
import reactor.core.publisher.Mono;
@Service
@@ -32,20 +35,17 @@ public class QuickRepliesManagerService {
private final MemoryStoreConversationService memoryStoreConversationService;
private final FirestoreConversationService firestoreConversationService;
private final QuickReplyContentService quickReplyContentService;
- private final DataLossPrevention dataLossPrevention;
- private final String dlpTemplatePersistFlow;
+ private final ConversationManagerService conversationManagerService;
public QuickRepliesManagerService(
+ @Lazy ConversationManagerService conversationManagerService,
MemoryStoreConversationService memoryStoreConversationService,
FirestoreConversationService firestoreConversationService,
- QuickReplyContentService quickReplyContentService,
- DataLossPrevention dataLossPrevention,
- @Value("${google.cloud.dlp.dlpTemplatePersistFlow}") String dlpTemplatePersistFlow) {
+ QuickReplyContentService quickReplyContentService) {
+ this.conversationManagerService = conversationManagerService;
this.memoryStoreConversationService = memoryStoreConversationService;
this.firestoreConversationService = firestoreConversationService;
this.quickReplyContentService = quickReplyContentService;
- this.dataLossPrevention = dataLossPrevention;
- this.dlpTemplatePersistFlow = dlpTemplatePersistFlow;
}
public Mono startQuickReplySession(QuickReplyScreenRequestDTO externalRequest) {
@@ -88,13 +88,27 @@ public class QuickRepliesManagerService {
.flatMap(session -> {
String userId = session.userId();
String sessionId = session.sessionId();
- return dataLossPrevention.getObfuscatedString(externalRequest.message(), dlpTemplatePersistFlow)
- .flatMap(obfuscatedMessage -> {
- ConversationEntryDTO userEntry = ConversationEntryDTO.forUser(obfuscatedMessage);
- long userMessagesCount = session.entries().stream()
- .filter(e -> e.entity() == ConversationEntryEntity.USUARIO)
- .count();
+ ConversationEntryDTO userEntry = ConversationEntryDTO.forUser(externalRequest.message());
+
+ List entries = session.entries();
+ int lastInitIndex = IntStream.range(0, entries.size())
+ .map(i -> entries.size() - 1 - i)
+ .filter(i -> {
+ ConversationEntryDTO entry = entries.get(i);
+ return entry.entity() == ConversationEntryEntity.SISTEMA && entry.type() == ConversationEntryType.INICIO;
+ })
+ .findFirst()
+ .orElse(-1);
+
+ long userMessagesCount;
+ if (lastInitIndex != -1) {
+ userMessagesCount = entries.subList(lastInitIndex + 1, entries.size()).stream()
+ .filter(e -> e.entity() == ConversationEntryEntity.USUARIO)
+ .count();
+ } else {
+ userMessagesCount = 0;
+ }
if (userMessagesCount == 0) { // Is the first user message in the Quick-Replies flow
// This is the second message of the flow. Return the full list.
@@ -118,33 +132,32 @@ public class QuickRepliesManagerService {
.filter(p -> p.titulo().equalsIgnoreCase(externalRequest.message().trim()))
.toList();
- QuickReplyDTO responseQuickReplyDTO;
if (!matchedPreguntas.isEmpty()) {
- responseQuickReplyDTO = new QuickReplyDTO(quickReplyDTO.header(),
- matchedPreguntas);
- } else {
- responseQuickReplyDTO = new QuickReplyDTO(quickReplyDTO.header(),
- Collections.emptyList());
- }
+ // Matched question, return the answer
+ String respuesta = matchedPreguntas.get(0).respuesta();
+ QueryResultDTO queryResult = new QueryResultDTO(respuesta, null);
+ DetectIntentResponseDTO response = new DetectIntentResponseDTO(sessionId, queryResult, null);
- // End the quick reply flow by clearing the pantallaContexto
- return memoryStoreConversationService
+ return memoryStoreConversationService
.updateSession(session.withPantallaContexto(null))
.then(persistConversationTurn(userId, sessionId,
- ConversationEntryDTO.forAgentWithMessage(
- responseQuickReplyDTO.toString()),
+ ConversationEntryDTO.forAgentWithMessage(respuesta),
userPhoneNumber, null))
- .thenReturn(new DetectIntentResponseDTO(sessionId, null,
- responseQuickReplyDTO));
+ .thenReturn(response);
+ } else {
+ // No match, delegate to Dialogflow
+ return memoryStoreConversationService
+ .updateSession(session.withPantallaContexto(null))
+ .then(conversationManagerService.manageConversation(externalRequest));
+ }
});
} else {
// Should not happen. End the flow.
return memoryStoreConversationService.updateSession(session.withPantallaContexto(null))
.then(Mono.just(new DetectIntentResponseDTO(session.sessionId(), null,
- new QuickReplyDTO("Flow Error", Collections.emptyList()))));
+ new QuickReplyDTO("Flow Error", null, null, null, Collections.emptyList()))));
}
});
- });
}
private Mono persistConversationTurn(String userId, String sessionId, ConversationEntryDTO entry,
diff --git a/src/main/java/com/example/service/quickreplies/QuickReplyContentService.java b/src/main/java/com/example/service/quickreplies/QuickReplyContentService.java
index 9da8e9a..b785587 100644
--- a/src/main/java/com/example/service/quickreplies/QuickReplyContentService.java
+++ b/src/main/java/com/example/service/quickreplies/QuickReplyContentService.java
@@ -28,7 +28,7 @@ public class QuickReplyContentService {
logger.info("Fetching quick replies from Firestore for document: {}", collectionId);
if (collectionId == null || collectionId.isBlank()) {
logger.warn("collectionId is null or empty. Returning empty quick replies.");
- return Mono.just(new QuickReplyDTO("empty", Collections.emptyList()));
+ return Mono.just(new QuickReplyDTO("empty", null, null, null, Collections.emptyList()));
}
return Mono.fromCallable(() -> {
try {
@@ -45,11 +45,14 @@ public class QuickReplyContentService {
.filter(DocumentSnapshot::exists)
.map(document -> {
String header = document.getString("header");
+ String body = document.getString("body");
+ String button = document.getString("button");
+ String headerSection = document.getString("header_section");
List