UPDATE 24-sept

This commit is contained in:
PAVEL PALMA
2025-09-24 17:20:48 -06:00
parent 26cbe71d2f
commit 535a1d8ca0
4 changed files with 113 additions and 43 deletions

View File

@@ -2,24 +2,28 @@
* 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.base;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import jakarta.annotation.PostConstruct;
import java.io.IOException;
import java.io.Reader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.io.InputStream;
/**
* Service to classify message entries (user text input from DetectIntent) into predefined categories
* like "CONVERSATION" or "NOTIFICATION" using Gemini model.
* Classifies a user's text input into a predefined category using a Gemini
* model.
* It analyzes the user's query in the context of a conversation history and any
* relevant notifications to determine if the message is part of the ongoing
* dialogue
* or an interruption. The classification is used to route the request to the
* appropriate handler (e.g., a standard conversational flow or a specific
* notification processor).
*/
@Service
public class MessageEntryFilter {
@@ -46,27 +50,29 @@ public class MessageEntryFilter {
public static final String CATEGORY_NOTIFICATION = "NOTIFICATION";
public static final String CATEGORY_UNKNOWN = "UNKNOWN";
public static final String CATEGORY_ERROR = "ERROR";
private String promptTemplate;
public MessageEntryFilter(GeminiClientService geminiService) {
this.geminiService = Objects.requireNonNull(geminiService, "GeminiClientService cannot be null for MessageEntryFilter.");
this.geminiService = Objects.requireNonNull(geminiService,
"GeminiClientService cannot be null for MessageEntryFilter.");
}
@PostConstruct
public void loadPromptTemplate() {
try {
ClassPathResource resource = new ClassPathResource(promptFilePath);
try (Reader reader = new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8)) {
this.promptTemplate = FileCopyUtils.copyToString(reader);
try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(promptFilePath)) {
if (inputStream == null) {
throw new IOException("Resource not found: " + promptFilePath);
}
logger.info("Successfully loaded prompt template from '{}'.", promptFilePath);
byte[] fileBytes = inputStream.readAllBytes();
this.promptTemplate = new String(fileBytes, StandardCharsets.UTF_8);
logger.info("Successfully loaded prompt template from '" + promptFilePath + "'.");
} catch (IOException e) {
logger.error("Failed to load prompt template from '{}'. Please ensure the file exists.", promptFilePath, e);
logger.error("Failed to load prompt template from '" + promptFilePath
+ "'. Please ensure the file exists. Error: " + e.getMessage());
throw new IllegalStateException("Could not load prompt template.", e);
}
}
public String classifyMessage(String queryInputText, String notificationsJson, String conversationJson) {
if (queryInputText == null || queryInputText.isBlank()) {
logger.warn("Query input text for classification is null or blank. Returning {}.", CATEGORY_UNKNOWN);

View File

@@ -8,16 +8,24 @@ import com.example.service.notification.MemoryStoreNotificationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import jakarta.annotation.PostConstruct;
import java.io.IOException;
import java.io.Reader;
import java.io.InputStreamReader;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
/**
* Resolves the conversational context of a user query by leveraging a large
* language model (LLM). This service evaluates a user's question in the context
* of a specific notification and conversation history, then decides if the
* query
* can be answered by the LLM or if it should be handled by a standard
* Dialogflow agent.
* The class loads an LLM prompt from an external file and dynamically
* formats it with a user's query and other context to drive its decision-making
* process.
*/
@Service
public class NotificationContextResolver {
@@ -43,21 +51,24 @@ public class NotificationContextResolver {
private String promptTemplate;
public NotificationContextResolver(GeminiClientService geminiService, MemoryStoreNotificationService memoryStoreNotificationService) {
public NotificationContextResolver(GeminiClientService geminiService,
MemoryStoreNotificationService memoryStoreNotificationService) {
this.geminiService = Objects.requireNonNull(geminiService,
"GeminiClientService cannot be null for NotificationContextResolver.");
}
@PostConstruct
public void loadPromptTemplate() {
try {
ClassPathResource resource = new ClassPathResource(promptFilePath);
try (Reader reader = new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8)) {
this.promptTemplate = FileCopyUtils.copyToString(reader);
try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(promptFilePath)) {
if (inputStream == null) {
throw new IOException("Resource not found: " + promptFilePath);
}
logger.info("Successfully loaded prompt template from '{}'.", promptFilePath);
byte[] fileBytes = inputStream.readAllBytes();
this.promptTemplate = new String(fileBytes, StandardCharsets.UTF_8);
logger.info("Successfully loaded prompt template from '" + promptFilePath + "'.");
} catch (IOException e) {
logger.error("Failed to load prompt template from '{}'. Please ensure the file exists.", promptFilePath, e);
logger.error("Failed to load prompt template from '" + promptFilePath
+ "'. Please ensure the file exists. Error: " + e.getMessage());
throw new IllegalStateException("Could not load prompt template.", e);
}
}

View File

@@ -12,35 +12,32 @@ import com.google.cloud.firestore.DocumentReference;
import com.google.cloud.firestore.DocumentSnapshot;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
@Component
public class FirestoreDataImporter {
private static final Logger logger = LoggerFactory.getLogger(FirestoreDataImporter.class);
private static final String QUICK_REPLIES_COLLECTION_PATH_FORMAT = "artifacts/%s/quick-replies";
@Autowired
private FirestoreBaseRepository firestoreBaseRepository;
private final FirestoreBaseRepository firestoreBaseRepository;
private final ObjectMapper objectMapper;
@Autowired
private ObjectMapper objectMapper;
private final boolean isImporterEnabled;
@Autowired
private Environment env;
public FirestoreDataImporter(FirestoreBaseRepository firestoreBaseRepository, ObjectMapper objectMapper) {
this.firestoreBaseRepository = firestoreBaseRepository;
this.objectMapper = objectMapper;
this.isImporterEnabled = Boolean.parseBoolean(System.getProperty("firestore.data.importer.enabled"));
@PostConstruct
public void importDataOnStartup() {
if (Boolean.parseBoolean(env.getProperty("firestore.data.importer.enabled"))) {
}
public void runImport() {
if (isImporterEnabled) {
try {
importQuickReplies();
} catch (Exception e) {
@@ -91,4 +88,4 @@ public class FirestoreDataImporter {
}
}
}
}
}