Skip to content

Commit 4f03aa8

Browse files
authored
Improved ChatGpt requests and response handling (#1262)
* improved chatgpt requests and response handling * markdown hint * (removed extra space)
1 parent f215304 commit 4f03aa8

File tree

4 files changed

+61
-60
lines changed

4 files changed

+61
-60
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,11 @@ public void onSlashCommand(SlashCommandInteractionEvent event) {
8080
public void onModalSubmitted(ModalInteractionEvent event, List<String> args) {
8181
event.deferReply().queue();
8282

83-
String context = "";
8483
String question = event.getValue(QUESTION_INPUT).getAsString();
8584

86-
Optional<String> optional = chatGptService.ask(question, context);
87-
if (optional.isPresent()) {
85+
Optional<String> chatgptResponse =
86+
chatGptService.ask(question, "You may use markdown syntax for the response");
87+
if (chatgptResponse.isPresent()) {
8888
userIdToAskedAtCache.put(event.getMember().getId(), Instant.now());
8989
}
9090

@@ -93,7 +93,7 @@ public void onModalSubmitted(ModalInteractionEvent event, List<String> args) {
9393
Please try again later.
9494
""";
9595

96-
String response = optional.orElse(errorResponse);
96+
String response = chatgptResponse.orElse(errorResponse);
9797
SelfUser selfUser = event.getJDA().getSelfUser();
9898

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

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

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010

1111
import org.togetherjava.tjbot.config.Config;
1212

13+
import javax.annotation.Nullable;
14+
1315
import java.time.Duration;
1416
import java.util.List;
15-
import java.util.Objects;
1617
import java.util.Optional;
1718

1819
/**
@@ -91,37 +92,33 @@ public ChatGptService(Config config) {
9192
* @see <a href="https://platform.openai.com/docs/guides/chat/managing-tokens">ChatGPT
9293
* Tokens</a>.
9394
*/
94-
public Optional<String> ask(String question, String context) {
95+
public Optional<String> ask(String question, @Nullable String context) {
9596
if (isDisabled) {
9697
return Optional.empty();
9798
}
9899

100+
String contextText = context == null ? "" : ", Context: %s.".formatted(context);
101+
String fullQuestion = "(KEEP IT CONCISE, NOT MORE THAN 280 WORDS%s) - %s"
102+
.formatted(contextText, question);
103+
104+
ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), fullQuestion);
105+
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
106+
.model(AI_MODEL)
107+
.messages(List.of(chatMessage))
108+
.frequencyPenalty(FREQUENCY_PENALTY)
109+
.temperature(TEMPERATURE)
110+
.maxTokens(MAX_TOKENS)
111+
.n(MAX_NUMBER_OF_RESPONSES)
112+
.build();
113+
logger.debug("ChatGpt Request: {}", fullQuestion);
114+
115+
String response = null;
99116
try {
100-
String instructions = "KEEP IT CONCISE, NOT MORE THAN 280 WORDS";
101-
String questionWithContext = "context: Category %s on a Java Q&A discord server. %s %s"
102-
.formatted(context, instructions, question);
103-
ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(),
104-
Objects.requireNonNull(questionWithContext));
105-
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
106-
.model(AI_MODEL)
107-
.messages(List.of(chatMessage))
108-
.frequencyPenalty(FREQUENCY_PENALTY)
109-
.temperature(TEMPERATURE)
110-
.maxTokens(MAX_TOKENS)
111-
.n(MAX_NUMBER_OF_RESPONSES)
112-
.build();
113-
114-
String response = openAiService.createChatCompletion(chatCompletionRequest)
117+
response = openAiService.createChatCompletion(chatCompletionRequest)
115118
.getChoices()
116119
.getFirst()
117120
.getMessage()
118121
.getContent();
119-
120-
if (response == null) {
121-
return Optional.empty();
122-
}
123-
124-
return Optional.of(response);
125122
} catch (OpenAiHttpException openAiHttpException) {
126123
logger.warn(
127124
"There was an error using the OpenAI API: {} Code: {} Type: {} Status Code: {}",
@@ -131,6 +128,12 @@ public Optional<String> ask(String question, String context) {
131128
logger.warn("There was an error using the OpenAI API: {}",
132129
runtimeException.getMessage());
133130
}
134-
return Optional.empty();
131+
132+
logger.debug("ChatGpt Response: {}", response);
133+
if (response == null) {
134+
return Optional.empty();
135+
}
136+
137+
return Optional.of(response);
135138
}
136139
}

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

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
import java.util.Map;
3939
import java.util.Optional;
4040
import java.util.Set;
41+
import java.util.StringJoiner;
42+
import java.util.concurrent.atomic.AtomicReference;
4143
import java.util.function.Consumer;
4244
import java.util.function.Function;
4345
import java.util.function.Predicate;
@@ -127,36 +129,37 @@ public HelpSystemHelper(Config config, Database database, ChatGptService chatGpt
127129
RestAction<Message> constructChatGptAttempt(ThreadChannel threadChannel,
128130
String originalQuestion, ComponentIdInteractor componentIdInteractor) {
129131
Optional<String> questionOptional = prepareChatGptQuestion(threadChannel, originalQuestion);
130-
Optional<String> chatGPTAnswer;
132+
Optional<String> chatGptAnswer;
131133

132134
if (questionOptional.isEmpty()) {
133135
return useChatGptFallbackMessage(threadChannel);
134136
}
135137
String question = questionOptional.get();
136-
logger.debug("The final question sent to chatGPT: {}", question);
137138

138139
ForumTag defaultTag = threadChannel.getAppliedTags().getFirst();
139140
ForumTag matchingTag = getCategoryTagOfChannel(threadChannel).orElse(defaultTag);
140141

141-
String context = matchingTag.getName();
142-
chatGPTAnswer = chatGptService.ask(question, context);
142+
String context =
143+
"Category %s on a Java Q&A discord server. You may use markdown syntax for the response"
144+
.formatted(matchingTag.getName());
145+
chatGptAnswer = chatGptService.ask(question, context);
143146

144-
if (chatGPTAnswer.isEmpty()) {
147+
if (chatGptAnswer.isEmpty()) {
145148
return useChatGptFallbackMessage(threadChannel);
146149
}
147150

148-
StringBuilder idForDismissButton = new StringBuilder();
149-
RestAction<Message> message =
151+
AtomicReference<String> messageId = new AtomicReference<>("");
152+
RestAction<Message> post =
150153
mentionGuildSlashCommand(threadChannel.getGuild(), ChatGptCommand.COMMAND_NAME)
151154
.map("""
152155
Here is an AI assisted attempt to answer your question 🤖. Maybe it helps! \
153156
In any case, a human is on the way 👍. To continue talking to the AI, you can use \
154157
%s.
155158
"""::formatted)
156159
.flatMap(threadChannel::sendMessage)
157-
.onSuccess(m -> idForDismissButton.append(m.getId()));
160+
.onSuccess(message -> messageId.set(message.getId()));
158161

159-
String answer = chatGPTAnswer.orElseThrow();
162+
String answer = chatGptAnswer.orElseThrow();
160163
SelfUser selfUser = threadChannel.getJDA().getSelfUser();
161164

162165
int responseCharLimit = MessageEmbed.DESCRIPTION_MAX_LENGTH;
@@ -165,9 +168,8 @@ RestAction<Message> constructChatGptAttempt(ThreadChannel threadChannel,
165168
}
166169

167170
MessageEmbed responseEmbed = generateGptResponseEmbed(answer, selfUser, originalQuestion);
168-
return message.flatMap(any -> threadChannel.sendMessageEmbeds(responseEmbed)
169-
.addActionRow(
170-
generateDismissButton(componentIdInteractor, idForDismissButton.toString())));
171+
return post.flatMap(any -> threadChannel.sendMessageEmbeds(responseEmbed)
172+
.addActionRow(generateDismissButton(componentIdInteractor, messageId.get())));
171173
}
172174

173175
/**
@@ -204,24 +206,21 @@ private Button generateDismissButton(ComponentIdInteractor componentIdInteractor
204206

205207
private Optional<String> prepareChatGptQuestion(ThreadChannel threadChannel,
206208
String originalQuestion) {
209+
StringJoiner question = new StringJoiner(" - ");
210+
207211
String questionTitle = threadChannel.getName();
208-
StringBuilder questionBuilder = new StringBuilder(MAX_QUESTION_LENGTH);
212+
question.add(questionTitle);
213+
question.add(originalQuestion.substring(0,
214+
Math.min(originalQuestion.length(), MAX_QUESTION_LENGTH)));
209215

210-
if (originalQuestion.length() < MIN_QUESTION_LENGTH
211-
&& questionTitle.length() < MIN_QUESTION_LENGTH) {
216+
// Not enough content for meaningful responses
217+
if (question.length() < MIN_QUESTION_LENGTH) {
212218
return Optional.empty();
213219
}
214220

215-
questionBuilder.append(questionTitle).append(" ");
216-
originalQuestion = originalQuestion.substring(0, Math
217-
.min(MAX_QUESTION_LENGTH - questionBuilder.length(), originalQuestion.length()));
218-
219-
questionBuilder.append(originalQuestion);
220-
221-
questionBuilder.append(
222-
". If possible, get, maximum, 5 top links from reliable websites as references in markdown syntax. Put this message on top of the links list \"Here are some links that may help :\".");
223-
224-
return Optional.of(questionBuilder.toString());
221+
question.add(
222+
"Additionally to answering the question, provide 3 useful links (as markdown list) from reliable websites on the topic. Write \"Useful links:\" as title for this list.");
223+
return Optional.of(question.toString());
225224
}
226225

227226
private RestAction<Message> useChatGptFallbackMessage(ThreadChannel threadChannel) {

application/src/main/java/org/togetherjava/tjbot/features/moderation/TransferQuestionCommand.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import net.dv8tion.jda.api.exceptions.ErrorResponseException;
1818
import net.dv8tion.jda.api.interactions.commands.build.Commands;
1919
import net.dv8tion.jda.api.interactions.components.text.TextInput;
20-
import net.dv8tion.jda.api.interactions.components.text.TextInput.Builder;
2120
import net.dv8tion.jda.api.interactions.components.text.TextInputStyle;
2221
import net.dv8tion.jda.api.interactions.modals.Modal;
2322
import net.dv8tion.jda.api.requests.ErrorResponse;
@@ -86,7 +85,6 @@ public TransferQuestionCommand(Config config, ChatGptService chatGptService) {
8685

8786
@Override
8887
public void onMessageContext(MessageContextInteractionEvent event) {
89-
9088
if (isInvalidForTransfer(event)) {
9189
return;
9290
}
@@ -96,11 +94,12 @@ public void onMessageContext(MessageContextInteractionEvent event) {
9694
String originalChannelId = event.getTarget().getChannel().getId();
9795
String authorId = event.getTarget().getAuthor().getId();
9896
String mostCommonTag = tags.getFirst();
99-
String chatGptPrompt =
100-
"Summarize the following text into a concise title or heading not more than 4-5 words, remove quotations if any: %s"
97+
98+
String chatGptTitleRequest =
99+
"Summarize the following question into a concise title or heading not more than 5 words, remove quotations if any: %s"
101100
.formatted(originalMessage);
102-
Optional<String> chatGptResponse = chatGptService.ask(chatGptPrompt, "");
103-
String title = chatGptResponse.orElse(createTitle(originalMessage));
101+
Optional<String> chatGptTitle = chatGptService.ask(chatGptTitleRequest, null);
102+
String title = chatGptTitle.orElse(createTitle(originalMessage));
104103
if (title.length() > TITLE_MAX_LENGTH) {
105104
title = title.substring(0, TITLE_MAX_LENGTH);
106105
}
@@ -112,7 +111,7 @@ public void onMessageContext(MessageContextInteractionEvent event) {
112111
.setValue(title)
113112
.build();
114113

115-
Builder modalInputBuilder =
114+
TextInput.Builder modalInputBuilder =
116115
TextInput.create(MODAL_INPUT_ID, "Question", TextInputStyle.PARAGRAPH)
117116
.setRequiredRange(INPUT_MIN_LENGTH, INPUT_MAX_LENGTH)
118117
.setPlaceholder("Contents of the question");

0 commit comments

Comments
 (0)