diff --git a/pom.xml b/pom.xml
index dad285d..83982fd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
5.4.0
2023.0.0
6.4.0.RELEASE
- 6.1.20
+ 6.1.21
@@ -61,36 +61,88 @@
org.springframework.boot
spring-boot-starter-webflux
+
+
+ org.springframework
+ spring-core
+
+
+
+
+ org.springframework
+ spring-web
org.springdoc
springdoc-openapi-starter-webflux-ui
2.5.0
+
+
+ org.springframework
+ spring-core
+
+
com.google.cloud
spring-cloud-gcp-starter-data-firestore
+
+
+ org.springframework
+ spring-core
+
+
com.google.cloud
spring-cloud-gcp-data-firestore
+
+
+ org.springframework
+ spring-core
+
+
com.google.cloud
spring-cloud-gcp-starter-storage
+
+
+ org.springframework
+ spring-core
+
+
org.springframework.boot
spring-boot-starter-data-redis-reactive
+
+
+ org.springframework
+ spring-core
+
+
org.springframework.boot
spring-boot-starter-actuator
+
+
+ org.springframework
+ spring-core
+
+
org.springframework.boot
spring-boot-starter-test
test
+
+
+ org.springframework
+ spring-core
+
+
com.google.cloud
@@ -172,6 +224,10 @@
2.10.0
test
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
org.apache.commons
commons-lang3
diff --git a/src/main/java/com/example/service/base/MessageEntryFilter.java b/src/main/java/com/example/service/base/MessageEntryFilter.java
index 7d887b7..a021357 100644
--- a/src/main/java/com/example/service/base/MessageEntryFilter.java
+++ b/src/main/java/com/example/service/base/MessageEntryFilter.java
@@ -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);
diff --git a/src/main/java/com/example/service/base/NotificationContextResolver.java b/src/main/java/com/example/service/base/NotificationContextResolver.java
index c6fa4d2..758ccd1 100644
--- a/src/main/java/com/example/service/base/NotificationContextResolver.java
+++ b/src/main/java/com/example/service/base/NotificationContextResolver.java
@@ -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);
}
}
diff --git a/src/main/java/com/example/util/FirestoreDataImporter.java b/src/main/java/com/example/util/FirestoreDataImporter.java
index 285cd65..6c437dd 100644
--- a/src/main/java/com/example/util/FirestoreDataImporter.java
+++ b/src/main/java/com/example/util/FirestoreDataImporter.java
@@ -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 {
}
}
}
-}
+}
\ No newline at end of file