Initial Python rewrite
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user