Skip to content

Commit d161c97

Browse files
ankitsmt211Taz03
authored andcommitted
AI response should be in embed (#1046)
* 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
1 parent b2cd16d commit d161c97

File tree

6 files changed

+57
-160
lines changed

6 files changed

+57
-160
lines changed

application/src/main/java/org/togetherjava/tjbot/features/Features.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ public static Collection<Feature> createFeatures(JDA jda, Database database, Con
162162
features.add(new HelpThreadCommand(config, helpSystemHelper));
163163
features.add(new ReportCommand(config));
164164
features.add(new BookmarksCommand(bookmarksSystem));
165-
features.add(new ChatGptCommand(chatGptService));
165+
features.add(new ChatGptCommand(chatGptService, helpSystemHelper));
166166
features.add(new JShellCommand(jshellEval));
167167

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

application/src/main/java/org/togetherjava/tjbot/features/chatgpt/AIResponseParser.java

Lines changed: 0 additions & 82 deletions
This file was deleted.

application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptCommand.java

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import com.github.benmanes.caffeine.cache.Cache;
44
import com.github.benmanes.caffeine.cache.Caffeine;
5+
import net.dv8tion.jda.api.entities.MessageEmbed;
6+
import net.dv8tion.jda.api.entities.SelfUser;
57
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent;
68
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
79
import net.dv8tion.jda.api.interactions.components.text.TextInput;
@@ -10,6 +12,7 @@
1012

1113
import org.togetherjava.tjbot.features.CommandVisibility;
1214
import org.togetherjava.tjbot.features.SlashCommandAdapter;
15+
import org.togetherjava.tjbot.features.help.HelpSystemHelper;
1316

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

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

4348
this.chatGptService = chatGptService;
49+
this.helper = helper;
4450
}
4551

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

7783
String context = "";
78-
Optional<String[]> optional =
79-
chatGptService.ask(event.getValue(QUESTION_INPUT).getAsString(), context);
84+
String question = event.getValue(QUESTION_INPUT).getAsString();
85+
86+
Optional<String> optional = chatGptService.ask(question, context);
8087
if (optional.isPresent()) {
8188
userIdToAskedAtCache.put(event.getMember().getId(), Instant.now());
8289
}
8390

84-
String[] errorResponse = {"""
91+
String errorResponse = """
8592
An error has occurred while trying to communicate with ChatGPT.
8693
Please try again later.
87-
"""};
94+
""";
8895

89-
String[] response = optional.orElse(errorResponse);
90-
for (String message : response) {
91-
event.getHook().sendMessage(message).queue();
92-
}
96+
String response = optional.orElse(errorResponse);
97+
SelfUser selfUser = event.getJDA().getSelfUser();
98+
99+
MessageEmbed responseEmbed = helper.generateGptResponseEmbed(response, selfUser, question);
100+
101+
event.getHook().sendMessageEmbeds(responseEmbed).queue();
93102
}
94103
}

application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptService.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,11 @@ public ChatGptService(Config config) {
8787
* @param question The question being asked of ChatGPT. Max is {@value MAX_TOKENS} tokens.
8888
* @param context The category of asked question, to set the context(eg. Java, Database, Other
8989
* etc).
90-
* @return partitioned response from ChatGPT as a String array.
90+
* @return response from ChatGPT as a String.
9191
* @see <a href="https://platform.openai.com/docs/guides/chat/managing-tokens">ChatGPT
9292
* Tokens</a>.
9393
*/
94-
public Optional<String[]> ask(String question, String context) {
94+
public Optional<String> ask(String question, String context) {
9595
if (isDisabled) {
9696
return Optional.empty();
9797
}
@@ -121,7 +121,7 @@ public Optional<String[]> ask(String question, String context) {
121121
return Optional.empty();
122122
}
123123

124-
return Optional.of(AIResponseParser.parse(response));
124+
return Optional.of(response);
125125
} catch (OpenAiHttpException openAiHttpException) {
126126
logger.warn(
127127
"There was an error using the OpenAI API: {} Code: {} Type: {} Status Code: {}",

application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.togetherjava.tjbot.features.help;
22

3+
import net.dv8tion.jda.api.EmbedBuilder;
34
import net.dv8tion.jda.api.entities.*;
45
import net.dv8tion.jda.api.entities.channel.attribute.IThreadContainer;
56
import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel;
@@ -9,7 +10,6 @@
910
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
1011
import net.dv8tion.jda.api.interactions.components.buttons.Button;
1112
import net.dv8tion.jda.api.requests.RestAction;
12-
import net.dv8tion.jda.api.requests.restaction.MessageCreateAction;
1313
import net.dv8tion.jda.internal.requests.CompletedRestAction;
1414
import org.slf4j.Logger;
1515
import org.slf4j.LoggerFactory;
@@ -33,7 +33,6 @@
3333
import java.util.Map;
3434
import java.util.Optional;
3535
import java.util.Set;
36-
import java.util.concurrent.CopyOnWriteArrayList;
3736
import java.util.function.Consumer;
3837
import java.util.function.Function;
3938
import java.util.function.Predicate;
@@ -117,7 +116,7 @@ public HelpSystemHelper(Config config, Database database, ChatGptService chatGpt
117116
RestAction<Message> constructChatGptAttempt(ThreadChannel threadChannel,
118117
String originalQuestion, ComponentIdInteractor componentIdInteractor) {
119118
Optional<String> questionOptional = prepareChatGptQuestion(threadChannel, originalQuestion);
120-
Optional<String[]> chatGPTAnswer;
119+
Optional<String> chatGPTAnswer;
121120

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

131130
String context = matchingTag.getName();
132131
chatGPTAnswer = chatGptService.ask(question, context);
132+
133133
if (chatGPTAnswer.isEmpty()) {
134134
return useChatGptFallbackMessage(threadChannel);
135135
}
136136

137-
List<String> ids = new CopyOnWriteArrayList<>();
137+
StringBuilder idForDismissButton = new StringBuilder();
138138
RestAction<Message> message =
139139
mentionGuildSlashCommand(threadChannel.getGuild(), ChatGptCommand.COMMAND_NAME)
140140
.map("""
@@ -143,27 +143,43 @@ RestAction<Message> constructChatGptAttempt(ThreadChannel threadChannel,
143143
%s.
144144
"""::formatted)
145145
.flatMap(threadChannel::sendMessage)
146-
.onSuccess(m -> ids.add(m.getId()));
147-
String[] answers = chatGPTAnswer.orElseThrow();
148-
149-
for (int i = 0; i < answers.length; i++) {
150-
MessageCreateAction answer = threadChannel.sendMessage(answers[i]);
146+
.onSuccess(m -> idForDismissButton.append(m.getId()));
151147

152-
if (i == answers.length - 1) {
153-
message = message.flatMap(any -> answer
154-
.addActionRow(generateDismissButton(componentIdInteractor, ids)));
155-
continue;
156-
}
148+
String answer = chatGPTAnswer.orElseThrow();
149+
SelfUser selfUser = threadChannel.getJDA().getSelfUser();
157150

158-
message = message.flatMap(ignored -> answer.onSuccess(m -> ids.add(m.getId())));
151+
int responseCharLimit = MessageEmbed.DESCRIPTION_MAX_LENGTH;
152+
if (answer.length() > responseCharLimit) {
153+
answer = answer.substring(0, responseCharLimit);
159154
}
160155

161-
return message;
156+
MessageEmbed responseEmbed = generateGptResponseEmbed(answer, selfUser, originalQuestion);
157+
return message.flatMap(any -> threadChannel.sendMessageEmbeds(responseEmbed)
158+
.addActionRow(
159+
generateDismissButton(componentIdInteractor, idForDismissButton.toString())));
160+
}
161+
162+
public MessageEmbed generateGptResponseEmbed(String answer, SelfUser selfUser, String title) {
163+
String responseByGptFooter = "- AI generated response";
164+
165+
int embedTitleLimit = MessageEmbed.TITLE_MAX_LENGTH;
166+
String capitalizedTitle = Character.toUpperCase(title.charAt(0)) + title.substring(1);
167+
168+
String titleForEmbed = capitalizedTitle.length() > embedTitleLimit
169+
? capitalizedTitle.substring(0, embedTitleLimit)
170+
: capitalizedTitle;
171+
172+
return new EmbedBuilder()
173+
.setAuthor(selfUser.getName(), null, selfUser.getEffectiveAvatarUrl())
174+
.setTitle(titleForEmbed)
175+
.setDescription(answer)
176+
.setColor(Color.pink)
177+
.setFooter(responseByGptFooter)
178+
.build();
162179
}
163180

164-
private Button generateDismissButton(ComponentIdInteractor componentIdInteractor,
165-
List<String> ids) {
166-
String buttonId = componentIdInteractor.generateComponentId(ids.toArray(String[]::new));
181+
private Button generateDismissButton(ComponentIdInteractor componentIdInteractor, String id) {
182+
String buttonId = componentIdInteractor.generateComponentId(id);
167183
return Button.danger(buttonId, "Dismiss");
168184
}
169185

application/src/test/java/org/togetherjava/tjbot/features/chatgpt/AIResponseParserTest.java

Lines changed: 0 additions & 46 deletions
This file was deleted.

0 commit comments

Comments
 (0)