From debea833d7751e47ed8c29ea4908f270a2cc8f53 Mon Sep 17 00:00:00 2001 From: Benjamin Sherriff Date: Mon, 17 Apr 2023 15:23:36 -0400 Subject: [PATCH] v0.1.23 Tweaking OpenAI settings, database tables --- .version | 2 +- pom.xml | 2 +- .../java/com/bensherriff/siren/ai/NLP.java | 112 +++++++----- .../bensherriff/siren/ai/OpenAIManager.java | 164 ++++++++---------- .../siren/database/DatabaseManager.java | 68 ++++++-- .../siren/database/MessageData.java | 130 +++++++++----- 6 files changed, 286 insertions(+), 192 deletions(-) diff --git a/.version b/.version index 06172af..00bd0d1 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -export SIREN_VERSION=0.1.22 \ No newline at end of file +export SIREN_VERSION=0.1.23 \ No newline at end of file diff --git a/pom.xml b/pom.xml index 0a366ab..f0f29ed 100644 --- a/pom.xml +++ b/pom.xml @@ -67,7 +67,7 @@ 2.14.2 0.12.0 42.6.0 - 4.5.3 + 4.2.0 2.0.7 2.20.0 UTF-8 diff --git a/src/main/java/com/bensherriff/siren/ai/NLP.java b/src/main/java/com/bensherriff/siren/ai/NLP.java index 0572bcb..b2e3a26 100644 --- a/src/main/java/com/bensherriff/siren/ai/NLP.java +++ b/src/main/java/com/bensherriff/siren/ai/NLP.java @@ -4,6 +4,7 @@ import edu.stanford.nlp.ling.CoreAnnotations; import edu.stanford.nlp.ling.CoreLabel; import edu.stanford.nlp.pipeline.Annotation; import edu.stanford.nlp.pipeline.StanfordCoreNLP; +import edu.stanford.nlp.sentiment.SentimentCoreAnnotations; import edu.stanford.nlp.util.CoreMap; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -17,57 +18,64 @@ public class NLP { public NLP() { Properties props = new Properties(); - props.setProperty("annotators", "tokenize, ssplit, pos, lemma, ner"); + props.setProperty("annotators", "tokenize, ssplit, pos, lemma, ner, parse, dcoref, sentiment"); pipeline = new StanfordCoreNLP(props); keywords = new HashMap<>(); keywords.put("dnd", Arrays.asList("dnd", "dungeons", "dragons", "sorcerer", "warlock", "cleric", "fighter", "rogue", "bard", "wizard", "paladin", "ranger", "druid")); } - public Set getTopicKeywords(String sentence) { - Set topics = new LinkedHashSet<>(); - Annotation document = new Annotation(sentence); - pipeline.annotate(document); + private List getSentences(String text) { + Annotation annotation = new Annotation(text); + pipeline.annotate(annotation); + return annotation.get(CoreAnnotations.SentencesAnnotation.class); + } - List sentences = document.get(CoreAnnotations.SentencesAnnotation.class); - CoreMap sentenceMap = sentences.get(0); - List tokens = sentenceMap.get(CoreAnnotations.TokensAnnotation.class); - List namedEntities = new ArrayList<>(); + public Set getTags(String text) { + Set tags = new LinkedHashSet<>(); - for (CoreLabel token : tokens) { - String ne = token.get(CoreAnnotations.NamedEntityTagAnnotation.class); - if (!ne.equals("0")) { - namedEntities.add(token); - } - } - for (CoreLabel namedEntity : namedEntities) { - String ne = namedEntity.get(CoreAnnotations.NamedEntityTagAnnotation.class); - String word = namedEntity.word(); - if (ne.equals("PERSON") || ne.equals("ORGANIZATION")) { - topics.add(word); - } else if (ne.equals("LOCATION")) { - String[] posTags = word.split("_"); - for (String posTag : posTags) { - if (posTag.startsWith("N")) { - topics.add(word); - break; - } + List sentences = getSentences(text); + for (CoreMap sentence : sentences) { + List tokens = sentence.get(CoreAnnotations.TokensAnnotation.class); + List namedEntities = new ArrayList<>(); + + for (CoreLabel token : tokens) { + String ne = token.get(CoreAnnotations.NamedEntityTagAnnotation.class); + if (!ne.equals("0")) { + namedEntities.add(token); } - } else { - String pos = namedEntity.get(CoreAnnotations.PartOfSpeechAnnotation.class); - if (pos.startsWith("NN")) { - topics.add(word); - for (String keyword : keywords.keySet()) { - if (keywords.get(keyword).contains(word.toLowerCase())) { - topics.add(keyword); + if (token.equals(tokens.get(tokens.size() - 1)) && token.word().equals("?") || + (List.of("what", "when", "who", "where", "why", "how").contains(token.word()))) { + tags.add("question"); + } + } + + for (CoreLabel namedEntity : namedEntities) { + String ne = namedEntity.get(CoreAnnotations.NamedEntityTagAnnotation.class); + String word = namedEntity.word(); + if (ne.equals("PERSON") || ne.equals("ORGANIZATION")) { + tags.add(word); + } else if (ne.equals("LOCATION")) { + String[] posTags = word.split("_"); + for (String posTag : posTags) { + if (posTag.startsWith("N")) { + tags.add(word); + break; } } - if (Arrays.asList("dnd", "dungeons", "dragons", "sorcerer", "warlock", "cleric", "fighter", "rogue", "bard", "wizard", "paladin", "ranger", "druid").contains(word.toLowerCase())) { - topics.add("dnd"); + } else { + String pos = namedEntity.get(CoreAnnotations.PartOfSpeechAnnotation.class); + if (pos.startsWith("NN")) { + tags.add(word); + for (String keyword : keywords.keySet()) { + if (keywords.get(keyword).contains(word.toLowerCase())) { + tags.add(keyword); + } + } } } } } - return topics; + return tags; } public List lemmatize(String documentText) { @@ -83,13 +91,31 @@ public class NLP { return lemmas; } - // TODO finish this method - public Set getSynonyms(String targetWord) { - return Collections.emptySet(); + /** + * Determines the sentiment (tone) of each sentence in the text. For example: positive, negative, or neutral + * @param text the input text + * @return the list of sentiments + */ + public List sentimentAnalysis(String text) { + Annotation document = new Annotation(text); + pipeline.annotate(document); + List sentences = document.get(CoreAnnotations.SentencesAnnotation.class); + List sentiments = new ArrayList<>(); + for (CoreMap sentence : sentences) { + sentiments.add(sentence.get(SentimentCoreAnnotations.SentimentClass.class)); + } + return sentiments; } - // TODO finish this method - public static double calculateSimilarity(String sentence1, String sentence2) { - return 0.0; + public static double cosineSimilarity(double[] vector1, double[] vector2) { + double dotProduct = 0.0; + double norm1 = 0.0; + double norm2 = 0.0; + for (int i = 0; i < vector1.length; i++) { + dotProduct += vector1[i] * vector2[i]; + norm1 += Math.pow(vector1[i], 2); + norm2 += Math.pow(vector2[i], 2); + } + return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2)); } } diff --git a/src/main/java/com/bensherriff/siren/ai/OpenAIManager.java b/src/main/java/com/bensherriff/siren/ai/OpenAIManager.java index f3165a3..724cc10 100644 --- a/src/main/java/com/bensherriff/siren/ai/OpenAIManager.java +++ b/src/main/java/com/bensherriff/siren/ai/OpenAIManager.java @@ -10,7 +10,6 @@ import com.theokanning.openai.completion.CompletionResult; import com.theokanning.openai.completion.chat.ChatCompletionRequest; import com.theokanning.openai.completion.chat.ChatCompletionResult; import com.theokanning.openai.completion.chat.ChatMessage; -import com.theokanning.openai.embedding.Embedding; import com.theokanning.openai.embedding.EmbeddingRequest; import com.theokanning.openai.image.CreateImageRequest; import com.theokanning.openai.image.ImageResult; @@ -88,34 +87,39 @@ public class OpenAIManager { userSettings = settings.getGuildSettings().get(guildId).getUserSettings().get(authorId); Model model = userSettings.getModel(); ChatMessage chatMessage = createUserMessage(message); - List embeddings = new ArrayList<>(); + LOGGER.trace("Guild: <{}> User: <{}> Message <{}>: {}", guildId, authorId, event.getMessageId(), message); - // Send OpenAI Message and get response - switch (model) { - case DAVINCI_3, DAVINCI_2, CURIE_1, BABBAGE_1, ADA_1 -> { - CompletionRequest completionRequest = createCompletionRequest(message, event); - CompletionResult completionResult = openAiService.createCompletion(completionRequest); - completionResult.getChoices().forEach(choice -> stringBuilder.append(choice.getText().trim())); - } - case GPT_4, GPT_4_32K, GPT_35_TURBO -> { - ChatCompletionRequest chatCompletionRequest = createCompletionRequest(chatMessage, event); - ChatCompletionResult chatCompletionResult = openAiService.createChatCompletion(chatCompletionRequest); - chatCompletionResult.getChoices().forEach(choice -> stringBuilder.append(choice.getMessage().getContent().trim())); - - //TODO fix embeddings -// EmbeddingRequest embeddingRequest = createEmbeddingRequest(chatMessages, event); -// EmbeddingResult embeddingResult = openAiService.createEmbeddings(embeddingRequest); -// embeddings.addAll(embeddingResult.getData()); - } - default -> { - event.getMessage().reply("Unexpected model in settings. Please contact " + owner + ".").queue(); - LOGGER.warn("Unexpected model in settings for guild {}: {}. Expected one of {}", guildId, - model, Arrays.toString(Model.values())); - return; + String query = new QueryBuilder("messages") + .where("guild_id = ? AND channel_id = ? AND prompt = ?") + .orderBy("timestamp DESC") + .build(); + List messages = DatabaseManager.parseResponse(DatabaseManager.query(query, + guildId, event.getChannel().getIdLong(), chatMessage.getContent())); + if (!messages.isEmpty()) { + stringBuilder.append(messages.get(0).getResponse()); + } else { + // Send OpenAI Message and get response + switch (model) { + case DAVINCI_3, DAVINCI_2, CURIE_1, BABBAGE_1, ADA_1 -> { + CompletionRequest completionRequest = createCompletionRequest(message, event); + CompletionResult completionResult = openAiService.createCompletion(completionRequest); + completionResult.getChoices().forEach(choice -> stringBuilder.append(choice.getText().trim())); + } + case GPT_4, GPT_4_32K, GPT_35_TURBO -> { + ChatCompletionRequest chatCompletionRequest = createCompletionRequest(chatMessage, event); + ChatCompletionResult chatCompletionResult = openAiService.createChatCompletion(chatCompletionRequest); + chatCompletionResult.getChoices().forEach(choice -> stringBuilder.append(choice.getMessage().getContent().trim())); + } + default -> { + event.getMessage().reply("Unexpected model in settings. Please contact " + owner + ".").queue(); + LOGGER.warn("Unexpected model in settings for guild {}: {}. Expected one of {}", guildId, + model, Arrays.toString(Model.values())); + return; + } } } - handleResponse(chatMessage, event, stringBuilder.toString(), embeddings); + handleResponse(chatMessage, event, stringBuilder.toString()); } catch (Exception ex) { LOGGER.error("Caught exception while processing message; {}", ex.getMessage()); event.getMessage().reply("An error occurred while processing your message. Please contact " + owner + ".").queue(); @@ -123,7 +127,6 @@ public class OpenAIManager { } private EmbeddingRequest createEmbeddingRequest(List chatMessages, MessageReceivedEvent event) { - GuildSettings guildSettings = settings.getGuildSettings().get(event.getGuild().getIdLong()); return EmbeddingRequest.builder() .model(Model.ADA_EMBEDDING_2.getName()) .user(event.getAuthor().getId()) @@ -144,66 +147,48 @@ public class OpenAIManager { UserSettings userSettings = settings.getGuildSettings().get(event.getGuild().getIdLong()) .getUserSettings().get(event.getAuthor().getIdLong()); List chatMessages = new ArrayList<>(); +// List previousMessages = getPreviousMessages(event); +// for (MessageData previousMessage : previousMessages) { +// chatMessages.add(createSystemMessage("In a previous conversation, we discussed the topics " + previousMessage.getTags() + +// " and " + previousMessage.getResponseTags())); +// chatMessages.add(createSystemMessage("I sent you the prompt \"" + previousMessage.getPrompt() + +// "\" and you replied with \"" + previousMessage.getResponse() + "\"")); +// } chatMessages.add(chatMessage); // Handle System Messages chatMessages.add(createSystemMessage("You are a discord bot named Siren")); chatMessages.add(createSystemMessage("My name is " + event.getAuthor().getName())); - if (event.isFromThread()) { - String query = new QueryBuilder("messages") - .where("guild_id = ? AND thread_id = ?") - .orderBy("timestamp DESC") - .limit(10) - .build(); - - // Build MessageData objects from query results - List> results = DatabaseManager.query( - query, event.getGuild().getIdLong(), event.getChannel().getIdLong()); - List previousMessages = new ArrayList<>(); - for (Map result : results) { - Object[] resultTopicObjects = (Object[]) ((PgArray) result.get("topics")).getArray(); - Set resultTopics = new HashSet<>(); - for (Object object : resultTopicObjects) { - resultTopics.add((String) object); - } - MessageData messageData = new MessageData( - (long) result.get("guild_id"), - (long) result.get("thread_id"), - (long) result.get("user_id"), - (String) result.get("message_type"), - (String) result.get("message_text"), - (String) result.get("message_response"), - resultTopics, - (Timestamp) result.get("timestamp") - ); - previousMessages.add(messageData); - } - - Set potentialTopics = new HashSet<>(); - for (MessageData previousMessage : previousMessages) { - ChatMessage previousChatMessage = createSystemMessage("For context, I previously sent you a message at " + - previousMessage.getTimestamp() + " which said \"" + previousMessage.getMessageText() + - "\". You replied with \"" + previousMessage.getMessageResponse() + "\"."); - potentialTopics.addAll(NLP.getTopicKeywords(previousMessage.getMessageText())); - potentialTopics.addAll(NLP.getTopicKeywords(previousMessage.getMessageResponse())); -// chatMessages.add(previousChatMessage); - } - LOGGER.trace("Potential topics: {}", potentialTopics); -// chatMessages.add(createSystemMessage("As an AI language model, only give replies that relate to " + topics)); - } return ChatCompletionRequest.builder() .model(userSettings.getModel().getName()) .maxTokens(userSettings.getMaxTokens()) .user(event.getAuthor().getId()) .temperature(settings.getOpenAISettings().getTemperature()) - .topP(settings.getOpenAISettings().getTopP()) +// .topP(settings.getOpenAISettings().getTopP()) .frequencyPenalty(settings.getOpenAISettings().getFrequencyPenalty()) .presencePenalty(settings.getOpenAISettings().getPresencePenalty()) .messages(chatMessages) .build(); } + private List getPreviousMessages(MessageReceivedEvent event) throws SQLException { + List previousMessages = new ArrayList<>(); + if (event.isFromThread()) { + String query = new QueryBuilder("messages") + .where("guild_id = ? AND channel_id = ?") + .orderBy("timestamp DESC") + .limit(3) + .build(); + + // Build MessageData objects from query results + List> results = DatabaseManager.query( + query, event.getGuild().getIdLong(), event.getChannel().getIdLong()); + previousMessages.addAll(DatabaseManager.parseResponse(results)); + } + return previousMessages; + } + private CompletionRequest createCompletionRequest(String message, MessageReceivedEvent event) { UserSettings userSettings = settings.getGuildSettings().get(event.getGuild().getIdLong()) .getUserSettings().get(event.getAuthor().getIdLong()); @@ -231,39 +216,40 @@ public class OpenAIManager { return new ChatMessage(Role.USER.getName(), message); } - private void handleResponse(ChatMessage chatMessage, MessageReceivedEvent event, String response, List embeddings) { + private void handleResponse(ChatMessage chatMessage, MessageReceivedEvent event, String response) { LOGGER.trace("Message Response <{}>: {}", event.getMessageId(), response); - Set topics = new LinkedHashSet<>(); - topics.addAll(NLP.getTopicKeywords(chatMessage.getContent())); - topics.addAll(NLP.getTopicKeywords(response)); - LOGGER.trace("Topics: {}", topics); + Set tags = NLP.getTags(chatMessage.getContent()); + Set responseTags = NLP.getTags(response); + + MessageData.MessageDataBuilder builder = new MessageData.MessageDataBuilder() + .guildId(event.getGuild().getIdLong()) + .channelId(event.getChannel().getIdLong()) + .userId(event.getAuthor().getIdLong()) + .type(chatMessage.getRole()) + .prompt(chatMessage.getContent()) + .response(response) + .tags(tags) + .responseTags(responseTags); + + LOGGER.trace("Tags: {}", tags); if (event.isFromThread()) { + DatabaseManager.storeMessage(builder.build()); ThreadChannel channel = event.getChannel().asThreadChannel(); - storeMessage(chatMessage, event, response, embeddings, topics, channel); + channel.sendMessage(response).queue(); } else { // The max discord title length is 100 characters String threadTitle = chatMessage.getContent(); if (chatMessage.getContent().length() > 100) { threadTitle = chatMessage.getContent().substring(0, 100); } - event.getMessage().createThreadChannel(threadTitle).queue(channel -> storeMessage(chatMessage, event, response, embeddings, topics, channel)); + event.getMessage().createThreadChannel(threadTitle).queue(channel -> { + DatabaseManager.storeMessage(builder.channelId(channel.getIdLong()).build()); + channel.sendMessage(response).queue(); + }); } } - private void storeMessage(ChatMessage chatMessage, MessageReceivedEvent event, String response, - List embeddings, Set topics, ThreadChannel channel) { - MessageData messageData = new MessageData(event.getGuild().getIdLong(), - channel.getIdLong(), event.getAuthor().getIdLong(), chatMessage.getRole(), chatMessage.getContent(), - response, topics); - int messageRow = DatabaseManager.storeMessage(messageData); - embeddings.forEach(embedding -> { - int embeddingRow = DatabaseManager.storeEmbedding(embedding.getEmbedding()); - DatabaseManager.storeMessageEmbeddings(messageRow, embeddingRow); - }); - channel.sendMessage(response).queue(); - } - private String parseMessage(String input) { return input.replaceAll("<@.*?>", "").replaceAll(" +", " ").trim(); } diff --git a/src/main/java/com/bensherriff/siren/database/DatabaseManager.java b/src/main/java/com/bensherriff/siren/database/DatabaseManager.java index a911ce1..63191a7 100644 --- a/src/main/java/com/bensherriff/siren/database/DatabaseManager.java +++ b/src/main/java/com/bensherriff/siren/database/DatabaseManager.java @@ -3,6 +3,7 @@ package com.bensherriff.siren.database; import com.bensherriff.siren.settings.LocalTrack; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.postgresql.jdbc.PgArray; import java.sql.*; import java.sql.Date; @@ -17,12 +18,13 @@ public class DatabaseManager { entry("messages", List.of( "id SERIAL PRIMARY KEY", "guild_id BIGINT NOT NULL", - "thread_id BIGINT NOT NULL", + "channel_id BIGINT NOT NULL", "user_id BIGINT NOT NULL", - "message_type VARCHAR(20) NOT NULL", - "message_text TEXT NOT NULL", - "message_response TEXT", - "topics TEXT[]", + "type VARCHAR(20) NOT NULL", + "prompt TEXT NOT NULL", + "response TEXT", + "tags TEXT[]", + "response_tags TEXT[]", "timestamp TIMESTAMP NOT NULL DEFAULT NOW()")), entry("embeddings", List.of( "id SERIAL PRIMARY KEY", @@ -81,22 +83,24 @@ public class DatabaseManager { public static int storeMessage(MessageData messageData) { String INSERT_MESSAGE = "INSERT INTO messages (" + - "message_type, " + "guild_id, " + - "thread_id, " + + "channel_id, " + "user_id, " + - "message_text, " + - "message_response, " + - "topics) " + - "VALUES (?, ?, ?, ?, ?, ?, ?)"; + "type, " + + "prompt, " + + "response, " + + "tags, " + + "response_tags) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; return storeMessage("messages", INSERT_MESSAGE, - messageData.getMessageType(), messageData.getGuildId(), - messageData.getThreadId(), + messageData.getChannelId(), messageData.getUserId(), - messageData.getMessageText(), - messageData.getMessageResponse(), - messageData.getTopics() + messageData.getType(), + messageData.getPrompt(), + messageData.getResponse(), + messageData.getTags(), + messageData.getResponseTags() ); } @@ -164,11 +168,11 @@ public class DatabaseManager { preparedStatement.setBlob(i, (Blob) param); } else if (param instanceof Clob) { preparedStatement.setClob(i, (Clob) param); - } else if (param instanceof List && ((List) param).get(0) instanceof String) { + } else if (param instanceof List && !((List) param).isEmpty() && ((List) param).get(0) instanceof String) { preparedStatement.setArray(i, connection.createArrayOf("text", ((List) param).toArray())); - } else if (param instanceof List && ((List) param).get(0) instanceof Double) { + } else if (param instanceof List && !((List) param).isEmpty() && ((List) param).get(0) instanceof Double) { preparedStatement.setArray(i, connection.createArrayOf("float8", ((List) param).toArray(new Double[0]))); - } else if (param instanceof Set && ((Set) param).toArray()[0] instanceof String) { + } else if (param instanceof Set && !((Set) param).isEmpty() && ((Set) param).toArray()[0] instanceof String) { preparedStatement.setArray(i, connection.createArrayOf("text", ((Set) param).toArray())); } else if (param instanceof String[]) { preparedStatement.setArray(i, connection.createArrayOf("text", ((String[]) param))); @@ -249,4 +253,30 @@ public class DatabaseManager { LOGGER.error("Failed to clear table; {}", ex.getMessage()); } } + + public static List parseResponse(List> results) throws SQLException { + List messageData = new ArrayList<>(); + for (Map result : results) { + Set promptTags = new HashSet<>(); + for (Object object : (Object[]) ((PgArray) result.get("tags")).getArray()) { + promptTags.add((String) object); + } + Set responseTags = new HashSet<>(); + for (Object object : (Object[]) ((PgArray) result.get("response_tags")).getArray()) { + promptTags.add((String) object); + } + messageData.add(new MessageData.MessageDataBuilder() + .guildId((long) result.get("guild_id")) + .channelId((long) result.get("channel_id")) + .userId((long) result.get("user_id")) + .type((String) result.get("type")) + .prompt((String) result.get("prompt")) + .response((String) result.get("response")) + .tags(promptTags) + .responseTags(responseTags) + .timestamp((Timestamp) result.get("timestamp")) + .build()); + } + return messageData; + } } diff --git a/src/main/java/com/bensherriff/siren/database/MessageData.java b/src/main/java/com/bensherriff/siren/database/MessageData.java index 8a42728..d15cb6c 100644 --- a/src/main/java/com/bensherriff/siren/database/MessageData.java +++ b/src/main/java/com/bensherriff/siren/database/MessageData.java @@ -1,74 +1,126 @@ package com.bensherriff.siren.database; import java.sql.Timestamp; +import java.util.LinkedHashSet; import java.util.Set; public class MessageData { private final Long guildId; - private final Long threadId; + private final Long channelId; private final Long userId; - private final String messageType; - private final String messageText; - private final String messageResponse; - private final Set topics; - private Timestamp timestamp; + private final String type; + private final String prompt; + private final String response; + private final Set tags; + private final Set responseTags; + private final Timestamp timestamp; - public MessageData(Long guildId, Long threadId, Long userId, String messageType, String messageText, - String messageResponse, Set topics) { - this.guildId = guildId; - this.threadId = threadId; - this.userId = userId; - this.messageType = messageType; - this.messageText = messageText; - this.messageResponse = messageResponse; - this.topics = topics; - } - - public MessageData(Long guildId, Long threadId, Long userId, String messageType, String messageText, - String messageResponse, Set topics, Timestamp timestamp) { - this.guildId = guildId; - this.threadId = threadId; - this.userId = userId; - this.messageType = messageType; - this.messageText = messageText; - this.messageResponse = messageResponse; - this.topics = topics; - this.timestamp = timestamp; + private MessageData(MessageDataBuilder builder) { + this.guildId = builder.guildId; + this.channelId = builder.channelId; + this.userId = builder.userId; + this.type = builder.type; + this.prompt = builder.prompt; + this.response = builder.response; + this.tags = builder.tags; + this.responseTags = builder.responseTags; + this.timestamp = builder.timestamp; } public Long getGuildId() { return guildId; } - public Long getThreadId() { - return threadId; + public Long getChannelId() { + return channelId; } public Long getUserId() { return userId; } - public String getMessageType() { - return messageType; + public String getType() { + return type; } - public String getMessageText() { - return messageText; + public String getPrompt() { + return prompt; } - public String getMessageResponse() { - return messageResponse; + public String getResponse() { + return response; } - public Set getTopics() { - return topics; + public Set getTags() { + return tags; + } + + public Set getResponseTags() { + return responseTags; } public Timestamp getTimestamp() { return timestamp; } - public void setTimestamp(Timestamp timestamp) { - this.timestamp = timestamp; + public static class MessageDataBuilder { + private Long guildId; + private Long channelId; + private Long userId; + private String type = ""; + private String prompt = ""; + private String response = ""; + private Set tags = new LinkedHashSet<>(); + private Set responseTags = new LinkedHashSet<>(); + private Timestamp timestamp = new Timestamp(0); + + public MessageDataBuilder guildId(Long guildId) { + this.guildId = guildId; + return this; + } + + public MessageDataBuilder channelId(Long channelId) { + this.channelId = channelId; + return this; + } + + public MessageDataBuilder userId(Long userId) { + this.userId = userId; + return this; + } + + public MessageDataBuilder type(String type) { + this.type = type; + return this; + } + + public MessageDataBuilder prompt(String prompt) { + this.prompt = prompt; + return this; + } + + public MessageDataBuilder response(String response) { + this.response = response; + return this; + } + + public MessageDataBuilder tags(Set tags) { + this.tags = tags; + return this; + } + + public MessageDataBuilder responseTags(Set responseTags) { + this.responseTags = responseTags; + return this; + } + + public MessageDataBuilder timestamp(Timestamp timestamp) { + this.timestamp = timestamp; + return this; + } + + public MessageData build() { + return new MessageData(this); + } } }