diff --git a/application/src/main/java/org/togetherjava/tjbot/features/Features.java b/application/src/main/java/org/togetherjava/tjbot/features/Features.java index 5cbfb7cea7..a2bfe00580 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/Features.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/Features.java @@ -88,9 +88,9 @@ public static Collection createFeatures(JDA jda, Database database, Con ModAuditLogWriter modAuditLogWriter = new ModAuditLogWriter(config); ScamHistoryStore scamHistoryStore = new ScamHistoryStore(database); GitHubReference githubReference = new GitHubReference(config); - CodeMessageHandler codeMessageHandler = - new CodeMessageHandler(blacklistConfig.special(), jshellEval); ChatGptService chatGptService = new ChatGptService(config); + CodeMessageHandler codeMessageHandler = + new CodeMessageHandler(blacklistConfig.special(), jshellEval, chatGptService); HelpSystemHelper helpSystemHelper = new HelpSystemHelper(config, database, chatGptService); // NOTE The system can add special system relevant commands also by itself, 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..96191b35db 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 @@ -45,6 +45,7 @@ public class ChatGptService { * a separate iteration based on the input. */ private static final int MAX_NUMBER_OF_RESPONSES = 1; + private static final int MAX_CODE_LENGTH = 2000; private static final String AI_MODEL = "gpt-3.5-turbo"; private boolean isDisabled = false; @@ -96,12 +97,51 @@ public Optional ask(String question, String context) { return Optional.empty(); } + 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); + + return getMessageResponse(questionWithContext); + } + + /** + * Provide ChatGPT with code to format. + * + * @param code the code to be formatted by ChatGPT. If code exceeds {@value MAX_CODE_LENGTH} + * characters then this method returns an empty {@link Optional} + * @return an optional response from ChatGPT as a String + */ + public Optional formatCode(CharSequence code) { + if (isDisabled || code.length() > MAX_CODE_LENGTH) { + return Optional.empty(); + } + + String payload = String.format( + """ + If you happen to find any code in the container below, FORMAT \ + IT regardless of the programming language you find. MAKE IT HUMANLY READABLE. \ + Output with no introduction, no explanation, no ``` stuff, only code. Double \ + check that your response is correct. The code provided might not be readable. + + --- BEGIN CODE --- + %s + --- END CODE --- + """, + code); + + return getMessageResponse(payload); + } + + /** + * Prompt ChatGPT with a message and get its response. + * + * @param message the message to send to ChatGPT + * @return an optional response from ChatGPT as a String + */ + private Optional getMessageResponse(String message) { 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)); + ChatMessage chatMessage = + new ChatMessage(ChatMessageRole.USER.value(), Objects.requireNonNull(message)); ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder() .model(AI_MODEL) .messages(List.of(chatMessage)) @@ -131,6 +171,7 @@ public Optional ask(String question, String context) { logger.warn("There was an error using the OpenAI API: {}", runtimeException.getMessage()); } + return Optional.empty(); } } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/code/ChatGPTFormatter.java b/application/src/main/java/org/togetherjava/tjbot/features/code/ChatGPTFormatter.java new file mode 100644 index 0000000000..2463f2e57b --- /dev/null +++ b/application/src/main/java/org/togetherjava/tjbot/features/code/ChatGPTFormatter.java @@ -0,0 +1,31 @@ +package org.togetherjava.tjbot.features.code; + +import org.togetherjava.tjbot.features.chatgpt.ChatGptService; + +import java.util.Optional; + +/** + * A utility class for formatting code using ChatGPT. + */ +public final class ChatGPTFormatter { + private final ChatGptService chatGptService; + + /** + * Constructs a new {@link ChatGPTFormatter} with the provided {@link ChatGptService}. + * + * @param chatGptService the {@link ChatGptService} used for formatting code + */ + public ChatGPTFormatter(ChatGptService chatGptService) { + this.chatGptService = chatGptService; + } + + /** + * Formats the provided code using ChatGPT. + * + * @param code the code to be formatted + * @return an Optional containing the formatted code if successful, empty otherwise + */ + public Optional format(CharSequence code) { + return chatGptService.formatCode(code); + } +} diff --git a/application/src/main/java/org/togetherjava/tjbot/features/code/CodeMessageHandler.java b/application/src/main/java/org/togetherjava/tjbot/features/code/CodeMessageHandler.java index bf9e8f54cd..8637447bc5 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/code/CodeMessageHandler.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/code/CodeMessageHandler.java @@ -17,6 +17,7 @@ import org.togetherjava.tjbot.features.MessageReceiverAdapter; import org.togetherjava.tjbot.features.UserInteractionType; import org.togetherjava.tjbot.features.UserInteractor; +import org.togetherjava.tjbot.features.chatgpt.ChatGptService; import org.togetherjava.tjbot.features.componentids.ComponentIdGenerator; import org.togetherjava.tjbot.features.componentids.ComponentIdInteractor; import org.togetherjava.tjbot.features.jshell.JShellEval; @@ -68,11 +69,14 @@ public final class CodeMessageHandler extends MessageReceiverAdapter implements * disabled * @param jshellEval used to execute java code and build visual result */ - public CodeMessageHandler(FeatureBlacklist blacklist, JShellEval jshellEval) { + public CodeMessageHandler(FeatureBlacklist blacklist, JShellEval jshellEval, + ChatGptService chatGptService) { componentIdInteractor = new ComponentIdInteractor(getInteractionType(), getName()); List codeActions = blacklist - .filterStream(Stream.of(new FormatCodeCommand(), new EvalCodeCommand(jshellEval)), + .filterStream( + Stream.of(new FormatCodeCommand(chatGptService), + new EvalCodeCommand(jshellEval)), codeAction -> codeAction.getClass().getName()) .toList(); diff --git a/application/src/main/java/org/togetherjava/tjbot/features/code/FormatCodeCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/code/FormatCodeCommand.java index 2072daf6ba..a2849312d7 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/code/FormatCodeCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/code/FormatCodeCommand.java @@ -3,6 +3,7 @@ import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.MessageEmbed; +import org.togetherjava.tjbot.features.chatgpt.ChatGptService; import org.togetherjava.tjbot.features.utils.CodeFence; import org.togetherjava.tjbot.formatter.Formatter; @@ -13,6 +14,16 @@ */ final class FormatCodeCommand implements CodeAction { private final Formatter formatter = new Formatter(); + private final ChatGPTFormatter chatGPTFormatter; + + /** + * Initializes a {@link FormatCodeCommand} which formats code. + * + * @param service the {@link ChatGptService} instance to be used for code formatting + */ + public FormatCodeCommand(ChatGptService service) { + this.chatGPTFormatter = new ChatGPTFormatter(service); + } @Override public String getLabel() { @@ -33,7 +44,13 @@ public MessageEmbed apply(CodeFence codeFence) { .build(); } + /** + * Formats the provided code using either the ChatGPT formatter or the generic formatter. + * + * @param code the code to be formatted + * @return the formatted code + */ private String formatCode(CharSequence code) { - return formatter.format(code); + return chatGPTFormatter.format(code).orElse(formatter.format(code)); } }