This commit is contained in:
2026-02-19 17:50:14 +00:00
parent da95a64fb7
commit 6f629c53a6
171 changed files with 7281 additions and 1144 deletions

View File

@@ -0,0 +1,128 @@
/*
* 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 com.example.service.notification.MemoryStoreNotificationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import jakarta.annotation.PostConstruct;
import java.io.IOException;
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 {
private static final Logger logger = LoggerFactory.getLogger(NotificationContextResolver.class);
private final GeminiClientService geminiService;
@Value("${notificationcontext.geminimodel:gemini-2.0-flash-001}")
private String geminiModelNameResolver;
@Value("${notificationcontext.temperature:0.1f}")
private Float resolverTemperature;
@Value("${notificationcontext.maxOutputTokens:1024}")
private Integer resolverMaxOutputTokens;
@Value("${notificationcontext.topP:0.1f}")
private Float resolverTopP;
@Value("${notificationcontext.prompt:prompts/notification_context_resolver.txt}")
private String promptFilePath;
public static final String CATEGORY_DIALOGFLOW = "DIALOGFLOW";
private String promptTemplate;
public NotificationContextResolver(GeminiClientService geminiService,
MemoryStoreNotificationService memoryStoreNotificationService) {
this.geminiService = Objects.requireNonNull(geminiService,
"GeminiClientService cannot be null for NotificationContextResolver.");
}
@PostConstruct
public void loadPromptTemplate() {
try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(promptFilePath)) {
if (inputStream == null) {
throw new IOException("Resource not found: " + 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 '" + promptFilePath
+ "'. Please ensure the file exists. Error: " + e.getMessage());
throw new IllegalStateException("Could not load prompt template.", e);
}
}
public String resolveContext(String queryInputText, String notificationsJson, String conversationJson,
String metadata, String userId, String sessionId, String userPhoneNumber) {
logger.debug("resolveContext -> queryInputText: {}, notificationsJson: {}, conversationJson: {}, metadata: {}",
queryInputText, notificationsJson, conversationJson, metadata);
if (queryInputText == null || queryInputText.isBlank()) {
logger.warn("Query input text for context resolution is null or blank.", CATEGORY_DIALOGFLOW);
return CATEGORY_DIALOGFLOW;
}
String notificationContent = (notificationsJson != null && !notificationsJson.isBlank()) ? notificationsJson
: "No metadata in notification.";
String conversationHistory = (conversationJson != null && !conversationJson.isBlank()) ? conversationJson
: "No conversation history.";
String contextPrompt = String.format(
this.promptTemplate,
conversationHistory,
notificationContent,
metadata,
queryInputText);
logger.debug("Sending context resolution request to Gemini for input (first 100 chars): '{}'...",
queryInputText.substring(0, Math.min(queryInputText.length(), 100)));
try {
String geminiResponse = geminiService.generateContent(
contextPrompt,
resolverTemperature,
resolverMaxOutputTokens,
geminiModelNameResolver,
resolverTopP);
if (geminiResponse != null && !geminiResponse.isBlank()) {
if (geminiResponse.trim().equalsIgnoreCase(CATEGORY_DIALOGFLOW)) {
logger.debug("Resolved to {}. Input: '{}'", CATEGORY_DIALOGFLOW, queryInputText);
return CATEGORY_DIALOGFLOW;
} else {
logger.debug("Resolved to a specific response. Input: '{}'", queryInputText);
return geminiResponse;
}
} else {
logger.warn("Gemini returned a null or blank response",
queryInputText, CATEGORY_DIALOGFLOW);
return CATEGORY_DIALOGFLOW;
}
} catch (Exception e) {
logger.error("An error occurred during Gemini content generation for context resolution.", e);
return CATEGORY_DIALOGFLOW;
}
}
}