UPDATE 24-sept
This commit is contained in:
58
pom.xml
58
pom.xml
@@ -21,7 +21,7 @@
|
|||||||
<spring-cloud-gcp.version>5.4.0</spring-cloud-gcp.version>
|
<spring-cloud-gcp.version>5.4.0</spring-cloud-gcp.version>
|
||||||
<spring-cloud.version>2023.0.0</spring-cloud.version>
|
<spring-cloud.version>2023.0.0</spring-cloud.version>
|
||||||
<lettuce.version>6.4.0.RELEASE</lettuce.version>
|
<lettuce.version>6.4.0.RELEASE</lettuce.version>
|
||||||
<spring-framework.version>6.1.20</spring-framework.version>
|
<spring-framework.version>6.1.21</spring-framework.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
@@ -61,36 +61,88 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-core</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-web</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springdoc</groupId>
|
<groupId>org.springdoc</groupId>
|
||||||
<artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
|
<artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
|
||||||
<version>2.5.0</version>
|
<version>2.5.0</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-core</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.cloud</groupId>
|
<groupId>com.google.cloud</groupId>
|
||||||
<artifactId>spring-cloud-gcp-starter-data-firestore</artifactId>
|
<artifactId>spring-cloud-gcp-starter-data-firestore</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-core</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.cloud</groupId>
|
<groupId>com.google.cloud</groupId>
|
||||||
<artifactId>spring-cloud-gcp-data-firestore</artifactId>
|
<artifactId>spring-cloud-gcp-data-firestore</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-core</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.cloud</groupId>
|
<groupId>com.google.cloud</groupId>
|
||||||
<artifactId>spring-cloud-gcp-starter-storage</artifactId>
|
<artifactId>spring-cloud-gcp-starter-storage</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-core</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
|
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-core</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-core</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-core</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.cloud</groupId>
|
<groupId>com.google.cloud</groupId>
|
||||||
@@ -172,6 +224,10 @@
|
|||||||
<version>2.10.0</version>
|
<version>2.10.0</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-lang3</artifactId>
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
|||||||
@@ -2,24 +2,28 @@
|
|||||||
* Copyright 2025 Google. This software is provided as-is, without warranty or representation for any use or purpose.
|
* 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.
|
* Your use of it is subject to your agreement with Google.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.example.service.base;
|
package com.example.service.base;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.FileCopyUtils;
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Reader;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service to classify message entries (user text input from DetectIntent) into predefined categories
|
* Classifies a user's text input into a predefined category using a Gemini
|
||||||
* like "CONVERSATION" or "NOTIFICATION" using Gemini model.
|
* 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
|
@Service
|
||||||
public class MessageEntryFilter {
|
public class MessageEntryFilter {
|
||||||
@@ -46,27 +50,29 @@ public class MessageEntryFilter {
|
|||||||
public static final String CATEGORY_NOTIFICATION = "NOTIFICATION";
|
public static final String CATEGORY_NOTIFICATION = "NOTIFICATION";
|
||||||
public static final String CATEGORY_UNKNOWN = "UNKNOWN";
|
public static final String CATEGORY_UNKNOWN = "UNKNOWN";
|
||||||
public static final String CATEGORY_ERROR = "ERROR";
|
public static final String CATEGORY_ERROR = "ERROR";
|
||||||
|
|
||||||
private String promptTemplate;
|
private String promptTemplate;
|
||||||
|
|
||||||
public MessageEntryFilter(GeminiClientService geminiService) {
|
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
|
@PostConstruct
|
||||||
public void loadPromptTemplate() {
|
public void loadPromptTemplate() {
|
||||||
try {
|
try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(promptFilePath)) {
|
||||||
ClassPathResource resource = new ClassPathResource(promptFilePath);
|
if (inputStream == null) {
|
||||||
try (Reader reader = new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8)) {
|
throw new IOException("Resource not found: " + promptFilePath);
|
||||||
this.promptTemplate = FileCopyUtils.copyToString(reader);
|
|
||||||
}
|
}
|
||||||
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) {
|
} 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);
|
throw new IllegalStateException("Could not load prompt template.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String classifyMessage(String queryInputText, String notificationsJson, String conversationJson) {
|
public String classifyMessage(String queryInputText, String notificationsJson, String conversationJson) {
|
||||||
if (queryInputText == null || queryInputText.isBlank()) {
|
if (queryInputText == null || queryInputText.isBlank()) {
|
||||||
logger.warn("Query input text for classification is null or blank. Returning {}.", CATEGORY_UNKNOWN);
|
logger.warn("Query input text for classification is null or blank. Returning {}.", CATEGORY_UNKNOWN);
|
||||||
|
|||||||
@@ -8,16 +8,24 @@ import com.example.service.notification.MemoryStoreNotificationService;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.FileCopyUtils;
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Objects;
|
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
|
@Service
|
||||||
public class NotificationContextResolver {
|
public class NotificationContextResolver {
|
||||||
|
|
||||||
@@ -43,21 +51,24 @@ public class NotificationContextResolver {
|
|||||||
|
|
||||||
private String promptTemplate;
|
private String promptTemplate;
|
||||||
|
|
||||||
public NotificationContextResolver(GeminiClientService geminiService, MemoryStoreNotificationService memoryStoreNotificationService) {
|
public NotificationContextResolver(GeminiClientService geminiService,
|
||||||
|
MemoryStoreNotificationService memoryStoreNotificationService) {
|
||||||
this.geminiService = Objects.requireNonNull(geminiService,
|
this.geminiService = Objects.requireNonNull(geminiService,
|
||||||
"GeminiClientService cannot be null for NotificationContextResolver.");
|
"GeminiClientService cannot be null for NotificationContextResolver.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void loadPromptTemplate() {
|
public void loadPromptTemplate() {
|
||||||
try {
|
try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(promptFilePath)) {
|
||||||
ClassPathResource resource = new ClassPathResource(promptFilePath);
|
if (inputStream == null) {
|
||||||
try (Reader reader = new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8)) {
|
throw new IOException("Resource not found: " + promptFilePath);
|
||||||
this.promptTemplate = FileCopyUtils.copyToString(reader);
|
|
||||||
}
|
}
|
||||||
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) {
|
} 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);
|
throw new IllegalStateException("Could not load prompt template.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,35 +12,32 @@ import com.google.cloud.firestore.DocumentReference;
|
|||||||
import com.google.cloud.firestore.DocumentSnapshot;
|
import com.google.cloud.firestore.DocumentSnapshot;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
@Component
|
|
||||||
public class FirestoreDataImporter {
|
public class FirestoreDataImporter {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(FirestoreDataImporter.class);
|
private static final Logger logger = LoggerFactory.getLogger(FirestoreDataImporter.class);
|
||||||
private static final String QUICK_REPLIES_COLLECTION_PATH_FORMAT = "artifacts/%s/quick-replies";
|
private static final String QUICK_REPLIES_COLLECTION_PATH_FORMAT = "artifacts/%s/quick-replies";
|
||||||
|
|
||||||
@Autowired
|
private final FirestoreBaseRepository firestoreBaseRepository;
|
||||||
private FirestoreBaseRepository firestoreBaseRepository;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
@Autowired
|
private final boolean isImporterEnabled;
|
||||||
private ObjectMapper objectMapper;
|
|
||||||
|
|
||||||
@Autowired
|
public FirestoreDataImporter(FirestoreBaseRepository firestoreBaseRepository, ObjectMapper objectMapper) {
|
||||||
private Environment env;
|
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 {
|
try {
|
||||||
importQuickReplies();
|
importQuickReplies();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -91,4 +88,4 @@ public class FirestoreDataImporter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user