diff --git a/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptCommand.java index d58c7d8377..163220d8a5 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptCommand.java @@ -80,11 +80,11 @@ public void onSlashCommand(SlashCommandInteractionEvent event) { public void onModalSubmitted(ModalInteractionEvent event, List args) { event.deferReply().queue(); - String context = ""; String question = event.getValue(QUESTION_INPUT).getAsString(); - Optional optional = chatGptService.ask(question, context); - if (optional.isPresent()) { + Optional chatgptResponse = + chatGptService.ask(question, "You may use markdown syntax for the response"); + if (chatgptResponse.isPresent()) { userIdToAskedAtCache.put(event.getMember().getId(), Instant.now()); } @@ -93,7 +93,7 @@ public void onModalSubmitted(ModalInteractionEvent event, List args) { Please try again later. """; - String response = optional.orElse(errorResponse); + String response = chatgptResponse.orElse(errorResponse); SelfUser selfUser = event.getJDA().getSelfUser(); MessageEmbed responseEmbed = helper.generateGptResponseEmbed(response, selfUser, question); diff --git a/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptService.java b/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptService.java index e8b02d04bb..a6fdcbcb9d 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptService.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptService.java @@ -10,9 +10,10 @@ import org.togetherjava.tjbot.config.Config; +import javax.annotation.Nullable; + import java.time.Duration; import java.util.List; -import java.util.Objects; import java.util.Optional; /** @@ -91,37 +92,33 @@ public ChatGptService(Config config) { * @see ChatGPT * Tokens. */ - public Optional ask(String question, String context) { + public Optional ask(String question, @Nullable String context) { if (isDisabled) { return Optional.empty(); } + String contextText = context == null ? "" : ", Context: %s.".formatted(context); + String fullQuestion = "(KEEP IT CONCISE, NOT MORE THAN 280 WORDS%s) - %s" + .formatted(contextText, question); + + ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), fullQuestion); + ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder() + .model(AI_MODEL) + .messages(List.of(chatMessage)) + .frequencyPenalty(FREQUENCY_PENALTY) + .temperature(TEMPERATURE) + .maxTokens(MAX_TOKENS) + .n(MAX_NUMBER_OF_RESPONSES) + .build(); + logger.debug("ChatGpt Request: {}", fullQuestion); + + String response = null; try { - String instructions = "KEEP IT CONCISE, NOT MORE THAN 280 WORDS"; - String questionWithContext = "context: Category %s on a Java Q&A discord server. %s %s" - .formatted(context, instructions, question); - ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), - Objects.requireNonNull(questionWithContext)); - ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder() - .model(AI_MODEL) - .messages(List.of(chatMessage)) - .frequencyPenalty(FREQUENCY_PENALTY) - .temperature(TEMPERATURE) - .maxTokens(MAX_TOKENS) - .n(MAX_NUMBER_OF_RESPONSES) - .build(); - - String response = openAiService.createChatCompletion(chatCompletionRequest) + response = openAiService.createChatCompletion(chatCompletionRequest) .getChoices() .getFirst() .getMessage() .getContent(); - - if (response == null) { - return Optional.empty(); - } - - return Optional.of(response); } catch (OpenAiHttpException openAiHttpException) { logger.warn( "There was an error using the OpenAI API: {} Code: {} Type: {} Status Code: {}", @@ -131,6 +128,12 @@ public Optional ask(String question, String context) { logger.warn("There was an error using the OpenAI API: {}", runtimeException.getMessage()); } - return Optional.empty(); + + logger.debug("ChatGpt Response: {}", response); + if (response == null) { + return Optional.empty(); + } + + return Optional.of(response); } } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java index 725b352a87..7f37d8f8cd 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java @@ -38,6 +38,8 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.StringJoiner; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -127,26 +129,27 @@ public HelpSystemHelper(Config config, Database database, ChatGptService chatGpt RestAction constructChatGptAttempt(ThreadChannel threadChannel, String originalQuestion, ComponentIdInteractor componentIdInteractor) { Optional questionOptional = prepareChatGptQuestion(threadChannel, originalQuestion); - Optional chatGPTAnswer; + Optional chatGptAnswer; if (questionOptional.isEmpty()) { return useChatGptFallbackMessage(threadChannel); } String question = questionOptional.get(); - logger.debug("The final question sent to chatGPT: {}", question); ForumTag defaultTag = threadChannel.getAppliedTags().getFirst(); ForumTag matchingTag = getCategoryTagOfChannel(threadChannel).orElse(defaultTag); - String context = matchingTag.getName(); - chatGPTAnswer = chatGptService.ask(question, context); + String context = + "Category %s on a Java Q&A discord server. You may use markdown syntax for the response" + .formatted(matchingTag.getName()); + chatGptAnswer = chatGptService.ask(question, context); - if (chatGPTAnswer.isEmpty()) { + if (chatGptAnswer.isEmpty()) { return useChatGptFallbackMessage(threadChannel); } - StringBuilder idForDismissButton = new StringBuilder(); - RestAction message = + AtomicReference messageId = new AtomicReference<>(""); + RestAction post = mentionGuildSlashCommand(threadChannel.getGuild(), ChatGptCommand.COMMAND_NAME) .map(""" Here is an AI assisted attempt to answer your question 🤖. Maybe it helps! \ @@ -154,9 +157,9 @@ RestAction constructChatGptAttempt(ThreadChannel threadChannel, %s. """::formatted) .flatMap(threadChannel::sendMessage) - .onSuccess(m -> idForDismissButton.append(m.getId())); + .onSuccess(message -> messageId.set(message.getId())); - String answer = chatGPTAnswer.orElseThrow(); + String answer = chatGptAnswer.orElseThrow(); SelfUser selfUser = threadChannel.getJDA().getSelfUser(); int responseCharLimit = MessageEmbed.DESCRIPTION_MAX_LENGTH; @@ -165,9 +168,8 @@ RestAction constructChatGptAttempt(ThreadChannel threadChannel, } MessageEmbed responseEmbed = generateGptResponseEmbed(answer, selfUser, originalQuestion); - return message.flatMap(any -> threadChannel.sendMessageEmbeds(responseEmbed) - .addActionRow( - generateDismissButton(componentIdInteractor, idForDismissButton.toString()))); + return post.flatMap(any -> threadChannel.sendMessageEmbeds(responseEmbed) + .addActionRow(generateDismissButton(componentIdInteractor, messageId.get()))); } /** @@ -204,24 +206,21 @@ private Button generateDismissButton(ComponentIdInteractor componentIdInteractor private Optional prepareChatGptQuestion(ThreadChannel threadChannel, String originalQuestion) { + StringJoiner question = new StringJoiner(" - "); + String questionTitle = threadChannel.getName(); - StringBuilder questionBuilder = new StringBuilder(MAX_QUESTION_LENGTH); + question.add(questionTitle); + question.add(originalQuestion.substring(0, + Math.min(originalQuestion.length(), MAX_QUESTION_LENGTH))); - if (originalQuestion.length() < MIN_QUESTION_LENGTH - && questionTitle.length() < MIN_QUESTION_LENGTH) { + // Not enough content for meaningful responses + if (question.length() < MIN_QUESTION_LENGTH) { return Optional.empty(); } - questionBuilder.append(questionTitle).append(" "); - originalQuestion = originalQuestion.substring(0, Math - .min(MAX_QUESTION_LENGTH - questionBuilder.length(), originalQuestion.length())); - - questionBuilder.append(originalQuestion); - - questionBuilder.append( - ". 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 :\"."); - - return Optional.of(questionBuilder.toString()); + question.add( + "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."); + return Optional.of(question.toString()); } private RestAction useChatGptFallbackMessage(ThreadChannel threadChannel) { diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/TransferQuestionCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/TransferQuestionCommand.java index 58ad3a5766..99e0ed8408 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/TransferQuestionCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/TransferQuestionCommand.java @@ -17,7 +17,6 @@ import net.dv8tion.jda.api.exceptions.ErrorResponseException; import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.components.text.TextInput; -import net.dv8tion.jda.api.interactions.components.text.TextInput.Builder; import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; import net.dv8tion.jda.api.interactions.modals.Modal; import net.dv8tion.jda.api.requests.ErrorResponse; @@ -86,7 +85,6 @@ public TransferQuestionCommand(Config config, ChatGptService chatGptService) { @Override public void onMessageContext(MessageContextInteractionEvent event) { - if (isInvalidForTransfer(event)) { return; } @@ -96,11 +94,12 @@ public void onMessageContext(MessageContextInteractionEvent event) { String originalChannelId = event.getTarget().getChannel().getId(); String authorId = event.getTarget().getAuthor().getId(); String mostCommonTag = tags.getFirst(); - String chatGptPrompt = - "Summarize the following text into a concise title or heading not more than 4-5 words, remove quotations if any: %s" + + String chatGptTitleRequest = + "Summarize the following question into a concise title or heading not more than 5 words, remove quotations if any: %s" .formatted(originalMessage); - Optional chatGptResponse = chatGptService.ask(chatGptPrompt, ""); - String title = chatGptResponse.orElse(createTitle(originalMessage)); + Optional chatGptTitle = chatGptService.ask(chatGptTitleRequest, null); + String title = chatGptTitle.orElse(createTitle(originalMessage)); if (title.length() > TITLE_MAX_LENGTH) { title = title.substring(0, TITLE_MAX_LENGTH); } @@ -112,7 +111,7 @@ public void onMessageContext(MessageContextInteractionEvent event) { .setValue(title) .build(); - Builder modalInputBuilder = + TextInput.Builder modalInputBuilder = TextInput.create(MODAL_INPUT_ID, "Question", TextInputStyle.PARAGRAPH) .setRequiredRange(INPUT_MIN_LENGTH, INPUT_MAX_LENGTH) .setPlaceholder("Contents of the question");