UPDATE 12-sept

This commit is contained in:
PAVEL PALMA
2025-09-12 16:09:42 -06:00
parent 62c09be67d
commit 6120b8d6c2
17 changed files with 89 additions and 375 deletions

View File

@@ -104,7 +104,7 @@ public class MessageEntryFilter {
String resultCategory = switch (geminiResponse != null ? geminiResponse.trim().toUpperCase() : "") {
case CATEGORY_CONVERSATION -> {
logger.info("Classified as {}. Input: '{}'", CATEGORY_CONVERSATION);
logger.debug("Classified as {}. Input: '{}'", CATEGORY_CONVERSATION, queryInputText);
logger.debug("Classified as {}. Input: '{}'", CATEGORY_CONVERSATION, queryInputText);
yield CATEGORY_CONVERSATION;
}
case CATEGORY_NOTIFICATION -> {

View File

@@ -104,7 +104,6 @@ public class FirestoreConversationService {
}
public Mono<ConversationSessionDTO> getSessionByTelefono(String userPhoneNumber) {
logger.info("Attempting to retrieve conversation session for phone number {}.", userPhoneNumber);
return firestoreBaseRepository.getDocumentsByField(getConversationCollectionPath(), "userPhoneNumber", userPhoneNumber)
.map(documentSnapshot -> {
if (documentSnapshot != null && documentSnapshot.exists()) {
@@ -112,7 +111,6 @@ public class FirestoreConversationService {
logger.info("Successfully retrieved and mapped conversation session for session {}.", sessionDTO.sessionId());
return sessionDTO;
}
logger.info("Conversation session not found for phone number {}.", userPhoneNumber);
return null;
});
}

View File

@@ -88,20 +88,18 @@ public class MemoryStoreConversationService {
return Mono.empty();
}
String phoneToSessionKey = PHONE_TO_SESSION_KEY_PREFIX + telefono;
logger.debug("Attempting to retrieve session ID for phone number {} from Memorystore.", telefono);
return stringRedisTemplate.opsForValue().get(phoneToSessionKey)
.flatMap(sessionId -> {
logger.debug("Found session ID {} for phone number {}. Retrieving session data.", sessionId, telefono);
return redisTemplate.opsForValue().get(SESSION_KEY_PREFIX + sessionId);
})
.doOnSuccess(session -> {
if (session != null) {
logger.info("Successfully retrieved session {} by phone number {}.", session.sessionId(), telefono);
logger.info("Successfully retrieved session by phone number");
} else {
logger.info("No session found in Redis for phone number {}.", telefono);
logger.info("No session found in Redis for phone number.");
}
})
.doOnError(e -> logger.error("Error retrieving session by phone number {}: {}", telefono, e.getMessage(), e));
.doOnError(e -> logger.error("Error retrieving session by phone number: {}", e));
}
public Mono<Void> updateSession(ConversationSessionDTO session) {

View File

@@ -95,12 +95,11 @@ public class FirestoreNotificationService {
}
} catch (ExecutionException e) {
logger.error(
"Error saving notification to Firestore for phone {}: {}",
phoneNumber,
"Error saving notification to Firestore for phone: {}",
e.getMessage(),
e);
throw new FirestorePersistenceException(
"Failed to save notification to Firestore for phone " + phoneNumber, e);
"Failed to save notification to Firestore for phone ", e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.error(
@@ -109,7 +108,7 @@ public class FirestoreNotificationService {
e.getMessage(),
e);
throw new FirestorePersistenceException(
"Saving notification was interrupted for phone " + phoneNumber, e);
"Saving notification was interrupted for phone ", e);
}
}
})

View File

@@ -111,12 +111,12 @@ public class MemoryStoreNotificationService {
return stringRedisTemplate.opsForValue().get(key)
.doOnSuccess(sessionId -> {
if (sessionId != null) {
logger.info("Session ID {} found for phone {}.", sessionId, phone);
logger.info("Session ID {} found for phone.", sessionId);
} else {
logger.debug("Session ID not found for phone {}.", phone);
logger.debug("Session ID not found for phone.");
}
})
.doOnError(e -> logger.error("Error retrieving session ID for phone {} from MemoryStore: {}", phone,
.doOnError(e -> logger.error("Error retrieving session ID for phone from MemoryStore: {}",
e.getMessage(), e));
}
@@ -144,19 +144,18 @@ public class MemoryStoreNotificationService {
return Mono.empty();
}
String phoneToSessionKey = PHONE_TO_CONVERSATION_SESSION_KEY_PREFIX + telefono;
logger.debug("Attempting to retrieve session ID for phone number {} from Redis.", telefono);
return stringRedisTemplate.opsForValue().get(phoneToSessionKey)
.flatMap(sessionId -> {
logger.debug("Found session ID {} for phone number {}. Retrieving session data.", sessionId, telefono);
logger.debug("Found session ID {} for phone number. Retrieving session data.", sessionId);
return conversationRedisTemplate.opsForValue().get(CONVERSATION_SESSION_KEY_PREFIX + sessionId);
})
.doOnSuccess(session -> {
if (session != null) {
logger.info("Successfully retrieved session {} by phone number {}.", session.sessionId(), telefono);
logger.info("Successfully retrieved session {} by phone number.", session.sessionId());
} else {
logger.info("No session found in Redis for phone number {}.", telefono);
logger.info("No session found in Redis for phone number.");
}
})
.doOnError(e -> logger.error("Error retrieving session by phone number {}: {}", telefono, e.getMessage(), e));
.doOnError(e -> logger.error("Error retrieving session by phone number: {}",e.getMessage(), e));
}
}

View File

@@ -93,11 +93,9 @@ public class NotificationManagerService {
firestoreNotificationService.saveOrAppendNotificationEntry(newNotificationEntry)
.subscribe(
ignored -> logger.debug(
"Background: Notification entry persistence initiated for phone {} in Firestore.",
telefono),
"Background: Notification entry persistence initiated for phone in Firestore."),
e -> logger.error(
"Background: Error during notification entry persistence for phone {} in Firestore: {}",
telefono, e.getMessage(), e));
"Background: Error during notification entry persistence for phone in Firestore: {}", e.getMessage(), e));
});
// 2. Resolve or create a conversation session
@@ -117,8 +115,7 @@ public class NotificationManagerService {
})
.switchIfEmpty(Mono.defer(() -> {
String newSessionId = SessionIdGenerator.generateStandardSessionId();
logger.info("No existing conversation session found for phone number {}. Creating new session: {}",
telefono, newSessionId);
logger.info("No existing conversation session found for phone number. Creating new session: {}", newSessionId);
String userId = "user_by_phone_" + telefono;
Map<String, Object> prefixedParameters = new HashMap<>();
if (obfuscatedRequest.hiddenParameters() != null) {
@@ -143,7 +140,7 @@ public class NotificationManagerService {
return dialogflowClientService.detectIntent(sessionId, detectIntentRequest);
})
.doOnSuccess(response -> logger
.info("Finished processing notification. Dialogflow response received for phone {}.", telefono))
.info("Finished processing notification. Dialogflow response received for phone"))
.doOnError(e -> logger.error("Overall error in NotificationManagerService: {}", e.getMessage(), e));
});
}

View File

@@ -68,7 +68,6 @@ public class MemoryStoreQRService {
return Mono.empty();
}
String phoneToSessionKey = PHONE_TO_SESSION_KEY_PREFIX + telefono;
logger.debug("Attempting to retrieve quick reply session ID for phone number {} from Redis.", telefono);
return stringRedisTemplate.opsForValue().get(phoneToSessionKey)
.flatMap(sessionId -> {
logger.debug("Found quick reply session ID {} for phone number {}. Retrieving session data.",
@@ -77,13 +76,12 @@ public class MemoryStoreQRService {
})
.doOnSuccess(session -> {
if (session != null) {
logger.info("Successfully retrieved quick reply session {} by phone number {}.",
session.sessionId(), telefono);
logger.info("Successfully retrieved quick reply session {} by phone number",
session.sessionId());
} else {
logger.info("No quick reply session found in Redis for phone number {}.", telefono);
logger.info("No quick reply session found in Redis for phone number");
}
})
.doOnError(e -> logger.error("Error retrieving quick reply session by phone number {}: {}", telefono,
e.getMessage(), e));
.doOnError(e -> logger.error("Error retrieving quick reply session by phone numbe: {}",e.getMessage(), e));
}
}

View File

@@ -84,7 +84,7 @@ public class QuickRepliesManagerService {
return memoryStoreConversationService.getSessionByTelefono(userPhoneNumber)
.switchIfEmpty(Mono.error(
new IllegalStateException("No quick reply session found for phone number: " + userPhoneNumber)))
new IllegalStateException("No quick reply session found for phone number" )))
.flatMap(session -> {
String userId = session.userId();
String sessionId = session.sessionId();
@@ -93,13 +93,14 @@ public class QuickRepliesManagerService {
List<ConversationEntryDTO> 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);
.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) {
@@ -136,19 +137,20 @@ public class QuickRepliesManagerService {
// Matched question, return the answer
String respuesta = matchedPreguntas.get(0).respuesta();
QueryResultDTO queryResult = new QueryResultDTO(respuesta, null);
DetectIntentResponseDTO response = new DetectIntentResponseDTO(sessionId, queryResult, null);
DetectIntentResponseDTO response = new DetectIntentResponseDTO(sessionId,
queryResult, null);
return memoryStoreConversationService
.updateSession(session.withPantallaContexto(null))
.then(persistConversationTurn(userId, sessionId,
ConversationEntryDTO.forAgentWithMessage(respuesta),
userPhoneNumber, null))
.thenReturn(response);
.updateSession(session.withPantallaContexto(null))
.then(persistConversationTurn(userId, sessionId,
ConversationEntryDTO.forAgentWithMessage(respuesta),
userPhoneNumber, null))
.thenReturn(response);
} else {
// No match, delegate to Dialogflow
return memoryStoreConversationService
.updateSession(session.withPantallaContexto(null))
.then(conversationManagerService.manageConversation(externalRequest));
.updateSession(session.withPantallaContexto(null))
.then(conversationManagerService.manageConversation(externalRequest));
}
});
} else {

View File

@@ -1,132 +0,0 @@
/*
* 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.summary;
import com.example.dto.gemini.ConversationSummaryRequest;
import com.example.dto.gemini.ConversationSummaryResponse;
import com.example.dto.gemini.ConversationSessionSummaryDTO;
import com.example.dto.gemini.ConversationEntrySummaryDTO;
import com.example.repository.FirestoreBaseRepository;
import com.example.service.base.GeminiClientService;
import com.google.cloud.firestore.DocumentReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
@Service
public class ConversationSummaryService {
private static final Logger logger = LoggerFactory.getLogger(ConversationSummaryService.class);
private final GeminiClientService geminiService;
private final FirestoreBaseRepository firestoreBaseRepository;
private static final String CONVERSATION_COLLECTION_PATH_FORMAT = "artifacts/%s/conversations";
private static final String DEFAULT_GEMINI_MODEL_NAME = "gemini-2.0-flash-001";
private static final Float DEFAULT_TEMPERATURE = 0.7f;
private static final Integer DEFAULT_MAX_OUTPUT_TOKENS = 800;
private static final Float DEFAULT_tOPP = 0.1f;
public ConversationSummaryService(GeminiClientService geminiService, FirestoreBaseRepository firestoreBaseRepository) {
this.geminiService = geminiService;
this.firestoreBaseRepository = firestoreBaseRepository;
}
public ConversationSummaryResponse summarizeConversation(ConversationSummaryRequest request) {
if (request == null) {
logger.warn("Summarization request is null.");
return new ConversationSummaryResponse("Request cannot be null.");
}
if (request.sessionId() == null || request.sessionId().isBlank()) {
logger.warn("Session ID is missing in the summarization request.");
return new ConversationSummaryResponse("Session ID is required.");
}
if (request.prompt() == null || request.prompt().isBlank()) {
logger.warn("Prompt for summarization is missing in the request.");
return new ConversationSummaryResponse("Prompt for summarization is required.");
}
String sessionId = request.sessionId();
String summarizationPromptInstruction = request.prompt();
String actualModelName = (request.modelName() != null && !request.modelName().isBlank())
? request.modelName() : DEFAULT_GEMINI_MODEL_NAME;
Float actualTemperature = (request.temperature() != null)
? request.temperature() : DEFAULT_TEMPERATURE;
Integer actualMaxOutputTokens = (request.maxOutputTokens() != null)
? request.maxOutputTokens() : DEFAULT_MAX_OUTPUT_TOKENS;
Float actualTopP = (request.top_P() != null)
? request.top_P() : DEFAULT_tOPP;
String collectionPath = String.format(CONVERSATION_COLLECTION_PATH_FORMAT, firestoreBaseRepository.getAppId());
String documentId = sessionId;
logger.info("Fetching conversation from Firestore: Collection='{}', Document='{}'", collectionPath, documentId);
ConversationSessionSummaryDTO sessionSummary;
try {
DocumentReference docRef = firestoreBaseRepository.getDocumentReference(collectionPath, documentId);
sessionSummary = firestoreBaseRepository.getDocument(docRef, ConversationSessionSummaryDTO.class);
logger.debug("Retrieved ConversationSessionSummaryDTO after Firestore fetch: sessionId={}, entries size={}",
sessionSummary != null ? sessionSummary.sessionId() : "null",
sessionSummary != null && sessionSummary.entries() != null ? sessionSummary.entries().size() : "N/A (entries list is null)");
if (sessionSummary == null) {
logger.warn("Firestore document not found or could not be mapped: {}/{}", collectionPath, documentId);
return new ConversationSummaryResponse("Conversation document not found for session ID: " + sessionId);
}
List<ConversationEntrySummaryDTO> entries = sessionSummary.entries();
if (entries == null || entries.isEmpty()) {
logger.warn("No conversation entries found in document {}/{} for session ID: {}",
collectionPath, documentId, sessionId);
return new ConversationSummaryResponse("No conversation messages found in the document for session ID: " + sessionId);
}
List<String> conversationMessages = entries.stream()
.map(entry -> {
String type = entry.type().map(t -> t.name()).orElse("UNKNOWN_TYPE");
String timestampString = entry.timestamp() != null ? entry.timestamp().toDate().toInstant().toString() : "UNKNOWN_TIMESTAMP";
return String.format("[%s - %s] %s", type, timestampString, entry.text());
})
.collect(Collectors.toList());
String formattedConversation = String.join("\n", conversationMessages);
String fullPromptForGemini = summarizationPromptInstruction + "\n\n" + formattedConversation;
logger.info("Sending summarization request to Gemini with custom prompt (first 200 chars): \n{}",
fullPromptForGemini.substring(0, Math.min(fullPromptForGemini.length(), 200)) + "...");
String summaryText = geminiService.generateContent(
fullPromptForGemini,
actualTemperature,
actualMaxOutputTokens,
actualModelName,
actualTopP
);
if (summaryText == null || summaryText.trim().isEmpty()) {
logger.warn("Gemini returned an empty or null summary for the conversation.");
return new ConversationSummaryResponse("Could not generate a summary. The model returned no text.");
}
logger.info("Successfully generated summary for session ID: {}", sessionId);
return new ConversationSummaryResponse(summaryText);
} catch (InterruptedException | ExecutionException e) {
logger.error("Error accessing Firestore for session ID {}: {}", sessionId, e.getMessage(), e);
Thread.currentThread().interrupt();
return new ConversationSummaryResponse("Error accessing conversation data: " + e.getMessage());
} catch (Exception e) {
logger.error("An unexpected error occurred during summarization for session ID {}: {}", sessionId, e.getMessage(), e);
return new ConversationSummaryResponse("An unexpected error occurred during summarization: " + e.getMessage());
}
}
}