From a3f40d1408d2eadd428df1ecce7d215c4e6e3a36 Mon Sep 17 00:00:00 2001 From: marcos Date: Thu, 6 Aug 2026 03:32:31 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20Ai=20review=20fixes=20=E2=80=94=20quit-r?= =?UTF-8?q?ace,=20token=20overflow,=20comment=20+=20nits?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Guard conversations.remember() on asker==null: a PlayerQuitEvent forgets the history (carry-forward #6), and re-adding here after the quit would resurrect it. lastAnswer stays regardless so /ia corrigir can still correct the last answer. - Saturate the retry token ceiling: max_tokens near Integer.MAX_VALUE would overflow to a negative budget sent to the API. - Tighten the constructor comment: aiUrl/aiWikiChars/aiMemoryExchanges/ aiMemoryMinutes are baked; everything else (aiProfile, aiModel, aiMaxTokens, aiTemperature, aiInstructions, aiServerContext) is read live, not just aiProfile. - Drop unused import java.util.List; add trailing newline. --- .../java/dev/marcospaulo/canalhandia/Ai.java | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/main/java/dev/marcospaulo/canalhandia/Ai.java b/src/main/java/dev/marcospaulo/canalhandia/Ai.java index 67abae6..5fdad58 100644 --- a/src/main/java/dev/marcospaulo/canalhandia/Ai.java +++ b/src/main/java/dev/marcospaulo/canalhandia/Ai.java @@ -11,7 +11,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.time.LocalDate; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.UUID; @@ -89,9 +88,12 @@ final class Ai { this.conversations = new Conversations(settings.aiMemoryExchanges(), settings.aiMemoryMinutes()); this.corrections = new Corrections(new java.io.File(plugin.getDataFolder(), "correcoes.yml")); // Note: aiUrl(), aiWikiChars(), aiMemoryExchanges() and aiMemoryMinutes() - // are baked here at construction. Only aiProfile() is read live in the - // async body, which is correct — operators switch profiles live without - // a restart, and the other settings are not meant to be hot-swapped. + // are baked here at construction (passed to the MiniMax/Wiki/Conversations + // constructors and not re-read). Everything else — aiProfile(), aiModel(), + // aiMaxTokens(), aiTemperature(), aiInstructions(), aiServerContext() — is + // read live in the async body, so operators can hot-swap them. Profiles in + // particular switch live without a restart; the baked four are not meant + // to be hot-swapped. } /** True if a key is configured. Without one the module stays quiet. */ @@ -247,8 +249,11 @@ final class Ai { // occasionally drops a foreign word mid-sentence. Both are // worth one retry before giving up (carry-forwards #3 and #7). if (answer == null || AiText.hasForeignScript(answer)) { - answer = api.answer(key, settings.aiModel(), messages, - settings.aiMaxTokens() * 2, 0.1); + // Cap before doubling: an absurd ia.max-tokens near + // Integer.MAX_VALUE would overflow to a negative budget + // and be sent to the API. The default (1200) is unaffected. + int retryTokens = Math.min(settings.aiMaxTokens(), Integer.MAX_VALUE / 2) * 2; + answer = api.answer(key, settings.aiModel(), messages, retryTokens, 0.1); } if (answer != null && AiText.hasForeignScript(answer)) { plugin.getLogger().warning("Resposta descartada por idioma estrangeiro."); @@ -317,7 +322,13 @@ final class Ai { return; } String clean = AiText.sanitise(answer, settings.aiMaxAnswer()); - conversations.remember(askerId, question, clean); + // Only remember if the asker is still online: a PlayerQuitEvent forgets + // the player's history (carry-forward #6), and re-adding here after the + // quit would resurrect it. lastAnswer stays regardless, so /ia corrigir + // can still correct the last answer even after the asker left. + if (asker != null) { + conversations.remember(askerId, question, clean); + } lastAnswer = new Answered(askerId, question, clean); Component message = Msg.tag("IA", NamedTextColor.LIGHT_PURPLE) @@ -359,4 +370,4 @@ final class Ai { return askedToday; } -} \ No newline at end of file +}