Skip to content

Commit 311d060

Browse files
committed
feature "rewrite-msg command": applies changes due to Wazei's first review - Part 2;
1 parent a8a761e commit 311d060

File tree

2 files changed

+87
-63
lines changed

2 files changed

+87
-63
lines changed

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

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,11 @@ public ChatGptService(Config config) {
3939
boolean keyIsDefaultDescription = apiKey.startsWith("<") && apiKey.endsWith(">");
4040
if (apiKey.isBlank() || keyIsDefaultDescription) {
4141
isDisabled = true;
42+
logger.warn("ChatGPT service is disabled: API key is not configured");
4243
return;
4344
}
4445
openAIClient = OpenAIOkHttpClient.builder().apiKey(apiKey).timeout(TIMEOUT).build();
46+
logger.info("ChatGPT service initialized successfully");
4547
}
4648

4749
/**
@@ -56,10 +58,6 @@ public ChatGptService(Config config) {
5658
* Tokens</a>.
5759
*/
5860
public Optional<String> ask(String question, @Nullable String context, ChatGptModel chatModel) {
59-
if (isDisabled) {
60-
return Optional.empty();
61-
}
62-
6361
String contextText = context == null ? "" : ", Context: %s.".formatted(context);
6462
String inputPrompt = """
6563
For code supplied for review, refer to the old code supplied rather than
@@ -71,35 +69,71 @@ public Optional<String> ask(String question, @Nullable String context, ChatGptMo
7169
Question: %s
7270
""".formatted(contextText, question);
7371

74-
logger.debug("ChatGpt request: {}", inputPrompt);
72+
return sendPrompt(inputPrompt, chatModel);
73+
}
74+
75+
/**
76+
* Prompt ChatGPT with a raw prompt and receive a response without any prefix wrapping.
77+
* <p>
78+
* Use this method when you need full control over the prompt structure without the service's
79+
* opinionated formatting (e.g., for iterative refinement or specialized use cases).
80+
*
81+
* @param inputPrompt The raw prompt to send to ChatGPT. Max is {@value MAX_TOKENS} tokens.
82+
* @param chatModel The AI model to use for this request.
83+
* @return response from ChatGPT as a String.
84+
* @see <a href="https://platform.openai.com/docs/guides/chat/managing-tokens">ChatGPT
85+
* Tokens</a>.
86+
*/
87+
public Optional<String> askRaw(String inputPrompt, ChatGptModel chatModel) {
88+
return sendPrompt(inputPrompt, chatModel);
89+
}
90+
91+
/**
92+
* Sends a prompt to the ChatGPT API and returns the response.
93+
*
94+
* @param prompt The prompt to send to ChatGPT.
95+
* @param chatModel The AI model to use for this request.
96+
* @return response from ChatGPT as a String.
97+
*/
98+
private Optional<String> sendPrompt(String prompt, ChatGptModel chatModel) {
99+
if (isDisabled) {
100+
logger.warn("ChatGPT request attempted but service is disabled");
101+
return Optional.empty();
102+
}
103+
104+
logger.debug("ChatGpt request: {}", prompt);
75105

76-
String response = null;
77106
try {
78107
ResponseCreateParams params = ResponseCreateParams.builder()
79108
.model(chatModel.toChatModel())
80-
.input(inputPrompt)
109+
.input(prompt)
81110
.maxOutputTokens(MAX_TOKENS)
82111
.build();
83112

84113
Response chatGptResponse = openAIClient.responses().create(params);
85114

86-
response = chatGptResponse.output()
115+
String response = chatGptResponse.output()
87116
.stream()
88117
.flatMap(item -> item.message().stream())
89118
.flatMap(message -> message.content().stream())
90119
.flatMap(content -> content.outputText().stream())
91120
.map(ResponseOutputText::text)
92121
.collect(Collectors.joining("\n"));
93-
} catch (RuntimeException runtimeException) {
94-
logger.warn("There was an error using the OpenAI API: {}",
95-
runtimeException.getMessage());
96-
}
97122

98-
logger.debug("ChatGpt Response: {}", response);
99-
if (response == null) {
123+
logger.debug("ChatGpt Response: {}", response);
124+
125+
if (response.isBlank()) {
126+
logger.warn("ChatGPT returned an empty response");
127+
return Optional.empty();
128+
}
129+
130+
logger.debug("ChatGpt response received successfully, length: {} characters",
131+
response.length());
132+
return Optional.of(response);
133+
} catch (RuntimeException runtimeException) {
134+
logger.error("Error communicating with OpenAI API: {}", runtimeException.getMessage(),
135+
runtimeException);
100136
return Optional.empty();
101137
}
102-
103-
return Optional.of(response);
104138
}
105139
}

application/src/main/java/org/togetherjava/tjbot/features/messages/RewriteCommand.java

Lines changed: 37 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,11 @@
1515
import org.togetherjava.tjbot.features.chatgpt.ChatGptService;
1616

1717
import java.util.Arrays;
18-
import java.util.Objects;
1918
import java.util.Optional;
2019

2120
/**
22-
* The implemented command is {@code /rewrite-msg}, which allows users to have their message
23-
* rewritten in a clearer, more professional, or better structured form using AI.
21+
* The implemented command is {@code /rewrite}, which allows users to have their message rewritten
22+
* in a clearer, more professional, or better structured form using AI.
2423
* <p>
2524
* The rewritten message is shown as an ephemeral message visible only to the user who triggered the
2625
* command.
@@ -35,39 +34,33 @@ public final class RewriteCommand extends SlashCommandAdapter {
3534

3635
private static final int MAX_MESSAGE_LENGTH = Message.MAX_CONTENT_LENGTH;
3736
private static final int MIN_MESSAGE_LENGTH = 3;
38-
private static final ChatGptModel CHAT_GPT_MODEL = ChatGptModel.FASTEST;
3937

4038
private final ChatGptService chatGptService;
4139

42-
private static String createAiPrompt(String userMessage, MessageTone tone) {
43-
return """
44-
Rewrite the following message to make it clearer, more professional, \
45-
and better structured. Maintain the original meaning while improving the quality \
46-
of the writing. Do NOT use em-dashes (—). %s
47-
48-
IMPORTANT: The rewritten text MUST be no more than 2000 characters. \
49-
If needed, compress wording while preserving key details and intent.
50-
51-
If the message is already well-written, provide minor improvements.
52-
53-
Original message:
54-
%s""".stripIndent().formatted(tone.description, userMessage);
40+
private static ChatGptModel selectAiModel(MessageTone tone) {
41+
return switch (tone) {
42+
case CLEAR, PROFESSIONAL -> ChatGptModel.FASTEST;
43+
case DETAILED, TECHNICAL -> ChatGptModel.HIGH_QUALITY;
44+
};
5545
}
5646

57-
private static String buildOriginalMsgResponse(String userMessage, MessageTone tone) {
47+
private static String createAiPrompt(String userMessage, MessageTone tone) {
5848
return """
59-
**Original message (%s)**
49+
You are rewriting a Discord text chat message for clarity and professionalism.
50+
Keep it conversational and casual—NOT email or formal document format.
6051
61-
%s
62-
""".stripIndent().formatted(tone.displayName, userMessage);
63-
}
52+
Tone: %s
6453
65-
private static String buildRewrittenMsgResponse(String aiMessage, MessageTone tone) {
66-
return """
67-
**Rewritten message (%s)**
54+
Rewrite the message to:
55+
- Improve clarity and structure
56+
- Maintain the original meaning
57+
- Avoid em-dashes (—)
58+
- Stay under %d characters (strict limit)
59+
60+
If the message is already well-written, make only minor improvements.
6861
69-
%s
70-
""".stripIndent().formatted(tone.displayName, aiMessage);
62+
Message to rewrite:
63+
%s""".stripIndent().formatted(tone.description, MAX_MESSAGE_LENGTH, userMessage);
7164
}
7265

7366
/**
@@ -100,8 +93,13 @@ public RewriteCommand(ChatGptService chatGptService) {
10093
@Override
10194
public void onSlashCommand(SlashCommandInteractionEvent event) {
10295

103-
final String userMessage =
104-
Objects.requireNonNull(event.getOption(MESSAGE_OPTION)).getAsString();
96+
final OptionMapping messageOption = event.getOption(MESSAGE_OPTION);
97+
98+
if (messageOption == null) {
99+
throw new IllegalStateException("Required option '" + MESSAGE_OPTION + "' is missing");
100+
}
101+
102+
final String userMessage = messageOption.getAsString();
105103
final MessageTone tone = parseTone(event.getOption(TONE_OPTION));
106104

107105
event.deferReply(true).queue();
@@ -124,15 +122,7 @@ public void onSlashCommand(SlashCommandInteractionEvent event) {
124122

125123
logger.debug("Rewrite successful; rewritten message length: {}", rewrittenText.length());
126124

127-
event.getHook()
128-
.sendMessage(buildOriginalMsgResponse(userMessage, tone))
129-
.setEphemeral(true)
130-
.queue();
131-
132-
event.getHook()
133-
.sendMessage(buildRewrittenMsgResponse(rewrittenText, tone))
134-
.setEphemeral(true)
135-
.queue();
125+
event.getHook().sendMessage(rewrittenText).setEphemeral(true).queue();
136126
}
137127

138128
private MessageTone parseTone(@Nullable OptionMapping toneOption)
@@ -150,10 +140,11 @@ private MessageTone parseTone(@Nullable OptionMapping toneOption)
150140

151141
private Optional<String> rewrite(String userMessage, MessageTone tone) {
152142

143+
final ChatGptModel aiModel = selectAiModel(tone);
144+
153145
final String rewritePrompt = createAiPrompt(userMessage, tone);
154146

155-
Optional<String> attempt =
156-
chatGptService.ask(rewritePrompt, tone.displayName, CHAT_GPT_MODEL);
147+
Optional<String> attempt = chatGptService.askRaw(rewritePrompt, aiModel);
157148

158149
if (attempt.isEmpty()) {
159150
return attempt;
@@ -166,20 +157,19 @@ private Optional<String> rewrite(String userMessage, MessageTone tone) {
166157
}
167158

168159
logger.debug("Rewritten message exceeded {} characters; retrying with stricter constraint",
169-
Message.MAX_CONTENT_LENGTH);
160+
MAX_MESSAGE_LENGTH);
170161

171162
final String shortenPrompt = rewritePrompt
172-
+ "\n\nConstraint reminder: Your previous rewrite exceeded "
173-
+ Message.MAX_CONTENT_LENGTH
174-
+ " characters. Provide a revised rewrite strictly under "
175-
+ Message.MAX_CONTENT_LENGTH + " characters while preserving meaning and tone.";
163+
+ "\n\nConstraint reminder: Your previous rewrite exceeded " + MAX_MESSAGE_LENGTH
164+
+ " characters. Provide a revised rewrite strictly under " + MAX_MESSAGE_LENGTH
165+
+ " characters while preserving meaning and tone.";
176166

177-
return chatGptService.ask(shortenPrompt, tone.displayName, CHAT_GPT_MODEL);
167+
return chatGptService.askRaw(shortenPrompt, aiModel);
178168
}
179169

180170
private enum MessageTone {
181171
CLEAR("Clear", "Make it clear and easy to understand."),
182-
PRO("Pro", "Use a professional and polished tone."),
172+
PROFESSIONAL("Professional", "Use a professional and polished tone."),
183173
DETAILED("Detailed", "Expand with more detail and explanation."),
184174
TECHNICAL("Technical", "Use technical and specialized language where appropriate.");
185175

0 commit comments

Comments
 (0)