Skip to content

Commit

Permalink
AI response should be in embed (#1046)
Browse files Browse the repository at this point in the history
* AI response should be in embed
* helper method to generate embed
* remove AI parser class that partitioned responses & related tests
* refactor dismiss button on generated responses
* update docs
* remove AIResponseParserTest
  • Loading branch information
ankitsmt211 authored Mar 12, 2024
1 parent a6ac181 commit 6dd670a
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 160 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ public static Collection<Feature> createFeatures(JDA jda, Database database, Con
features.add(new HelpThreadCommand(config, helpSystemHelper));
features.add(new ReportCommand(config));
features.add(new BookmarksCommand(bookmarksSystem));
features.add(new ChatGptCommand(chatGptService));
features.add(new ChatGptCommand(chatGptService, helpSystemHelper));
features.add(new JShellCommand(jshellEval));

FeatureBlacklist<Class<?>> blacklist = blacklistConfig.normal();
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.SelfUser;
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.components.Modal;
Expand All @@ -10,6 +12,7 @@

import org.togetherjava.tjbot.features.CommandVisibility;
import org.togetherjava.tjbot.features.SlashCommandAdapter;
import org.togetherjava.tjbot.features.help.HelpSystemHelper;

import java.time.Duration;
import java.time.Instant;
Expand All @@ -28,6 +31,7 @@ public final class ChatGptCommand extends SlashCommandAdapter {
private static final int MIN_MESSAGE_INPUT_LENGTH = 4;
private static final Duration COMMAND_COOLDOWN = Duration.of(10, ChronoUnit.SECONDS);
private final ChatGptService chatGptService;
private final HelpSystemHelper helper;

private final Cache<String, Instant> userIdToAskedAtCache =
Caffeine.newBuilder().maximumSize(1_000).expireAfterWrite(COMMAND_COOLDOWN).build();
Expand All @@ -36,11 +40,13 @@ public final class ChatGptCommand extends SlashCommandAdapter {
* Creates an instance of the chatgpt command.
*
* @param chatGptService ChatGptService - Needed to make calls to ChatGPT API
* @param helper HelpSystemHelper - Needed to generate response embed for prompt
*/
public ChatGptCommand(ChatGptService chatGptService) {
public ChatGptCommand(ChatGptService chatGptService, HelpSystemHelper helper) {
super(COMMAND_NAME, "Ask the ChatGPT AI a question!", CommandVisibility.GUILD);

this.chatGptService = chatGptService;
this.helper = helper;
}

@Override
Expand Down Expand Up @@ -75,20 +81,23 @@ public void onModalSubmitted(ModalInteractionEvent event, List<String> args) {
event.deferReply().queue();

String context = "";
Optional<String[]> optional =
chatGptService.ask(event.getValue(QUESTION_INPUT).getAsString(), context);
String question = event.getValue(QUESTION_INPUT).getAsString();

Optional<String> optional = chatGptService.ask(question, context);
if (optional.isPresent()) {
userIdToAskedAtCache.put(event.getMember().getId(), Instant.now());
}

String[] errorResponse = {"""
String errorResponse = """
An error has occurred while trying to communicate with ChatGPT.
Please try again later.
"""};
""";

String[] response = optional.orElse(errorResponse);
for (String message : response) {
event.getHook().sendMessage(message).queue();
}
String response = optional.orElse(errorResponse);
SelfUser selfUser = event.getJDA().getSelfUser();

MessageEmbed responseEmbed = helper.generateGptResponseEmbed(response, selfUser, question);

event.getHook().sendMessageEmbeds(responseEmbed).queue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,11 @@ public ChatGptService(Config config) {
* @param question The question being asked of ChatGPT. Max is {@value MAX_TOKENS} tokens.
* @param context The category of asked question, to set the context(eg. Java, Database, Other
* etc).
* @return partitioned response from ChatGPT as a String array.
* @return response from ChatGPT as a String.
* @see <a href="https://platform.openai.com/docs/guides/chat/managing-tokens">ChatGPT
* Tokens</a>.
*/
public Optional<String[]> ask(String question, String context) {
public Optional<String> ask(String question, String context) {
if (isDisabled) {
return Optional.empty();
}
Expand Down Expand Up @@ -121,7 +121,7 @@ public Optional<String[]> ask(String question, String context) {
return Optional.empty();
}

return Optional.of(AIResponseParser.parse(response));
return Optional.of(response);
} catch (OpenAiHttpException openAiHttpException) {
logger.warn(
"There was an error using the OpenAI API: {} Code: {} Type: {} Status Code: {}",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.togetherjava.tjbot.features.help;

import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.entities.channel.attribute.IThreadContainer;
import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel;
Expand All @@ -9,7 +10,6 @@
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
import net.dv8tion.jda.api.interactions.components.buttons.Button;
import net.dv8tion.jda.api.requests.RestAction;
import net.dv8tion.jda.api.requests.restaction.MessageCreateAction;
import net.dv8tion.jda.internal.requests.CompletedRestAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -33,7 +33,6 @@
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
Expand Down Expand Up @@ -117,7 +116,7 @@ public HelpSystemHelper(Config config, Database database, ChatGptService chatGpt
RestAction<Message> constructChatGptAttempt(ThreadChannel threadChannel,
String originalQuestion, ComponentIdInteractor componentIdInteractor) {
Optional<String> questionOptional = prepareChatGptQuestion(threadChannel, originalQuestion);
Optional<String[]> chatGPTAnswer;
Optional<String> chatGPTAnswer;

if (questionOptional.isEmpty()) {
return useChatGptFallbackMessage(threadChannel);
Expand All @@ -130,11 +129,12 @@ RestAction<Message> constructChatGptAttempt(ThreadChannel threadChannel,

String context = matchingTag.getName();
chatGPTAnswer = chatGptService.ask(question, context);

if (chatGPTAnswer.isEmpty()) {
return useChatGptFallbackMessage(threadChannel);
}

List<String> ids = new CopyOnWriteArrayList<>();
StringBuilder idForDismissButton = new StringBuilder();
RestAction<Message> message =
mentionGuildSlashCommand(threadChannel.getGuild(), ChatGptCommand.COMMAND_NAME)
.map("""
Expand All @@ -143,27 +143,43 @@ RestAction<Message> constructChatGptAttempt(ThreadChannel threadChannel,
%s.
"""::formatted)
.flatMap(threadChannel::sendMessage)
.onSuccess(m -> ids.add(m.getId()));
String[] answers = chatGPTAnswer.orElseThrow();

for (int i = 0; i < answers.length; i++) {
MessageCreateAction answer = threadChannel.sendMessage(answers[i]);
.onSuccess(m -> idForDismissButton.append(m.getId()));

if (i == answers.length - 1) {
message = message.flatMap(any -> answer
.addActionRow(generateDismissButton(componentIdInteractor, ids)));
continue;
}
String answer = chatGPTAnswer.orElseThrow();
SelfUser selfUser = threadChannel.getJDA().getSelfUser();

message = message.flatMap(ignored -> answer.onSuccess(m -> ids.add(m.getId())));
int responseCharLimit = MessageEmbed.DESCRIPTION_MAX_LENGTH;
if (answer.length() > responseCharLimit) {
answer = answer.substring(0, responseCharLimit);
}

return message;
MessageEmbed responseEmbed = generateGptResponseEmbed(answer, selfUser, originalQuestion);
return message.flatMap(any -> threadChannel.sendMessageEmbeds(responseEmbed)
.addActionRow(
generateDismissButton(componentIdInteractor, idForDismissButton.toString())));
}

public MessageEmbed generateGptResponseEmbed(String answer, SelfUser selfUser, String title) {
String responseByGptFooter = "- AI generated response";

int embedTitleLimit = MessageEmbed.TITLE_MAX_LENGTH;
String capitalizedTitle = Character.toUpperCase(title.charAt(0)) + title.substring(1);

String titleForEmbed = capitalizedTitle.length() > embedTitleLimit
? capitalizedTitle.substring(0, embedTitleLimit)
: capitalizedTitle;

return new EmbedBuilder()
.setAuthor(selfUser.getName(), null, selfUser.getEffectiveAvatarUrl())
.setTitle(titleForEmbed)
.setDescription(answer)
.setColor(Color.pink)
.setFooter(responseByGptFooter)
.build();
}

private Button generateDismissButton(ComponentIdInteractor componentIdInteractor,
List<String> ids) {
String buttonId = componentIdInteractor.generateComponentId(ids.toArray(String[]::new));
private Button generateDismissButton(ComponentIdInteractor componentIdInteractor, String id) {
String buttonId = componentIdInteractor.generateComponentId(id);
return Button.danger(buttonId, "Dismiss");
}

Expand Down

This file was deleted.

0 comments on commit 6dd670a

Please sign in to comment.