fix: clean ||| from chat history and handle afterthoughts in reaction replies

- Extract _split_afterthought helper method
- Store cleaned content (no |||) in chat history to prevent LLM reinforcement
- Handle afterthought splitting in reaction-reply path too
- Log main_reply instead of raw response

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 11:33:11 -05:00
parent 6866ca8adf
commit 175c7ad219
+30 -16
View File
@@ -73,6 +73,19 @@ def _format_relative_time(dt: datetime) -> str:
class ChatCog(commands.Cog): class ChatCog(commands.Cog):
@staticmethod
def _split_afterthought(response: str) -> tuple[str, str | None]:
"""Split a response on ||| into (main_reply, afterthought)."""
if "|||" not in response:
return response, None
parts = response.split("|||", 1)
main = parts[0].strip()
after = parts[1].strip() or None
if not main:
return response, None
return main, after
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
# Per-channel conversation history for the bot: {channel_id: deque of {role, content}} # Per-channel conversation history for the bot: {channel_id: deque of {role, content}}
@@ -395,9 +408,14 @@ class ChatCog(commands.Cog):
logger.warning("LLM returned no response for %s in #%s", message.author, message.channel.name) logger.warning("LLM returned no response for %s in #%s", message.author, message.channel.name)
return return
# Split afterthoughts (triple-pipe delimiter)
main_reply, afterthought = self._split_afterthought(response)
# Store cleaned content in history (no ||| delimiter)
if not image_attachment: if not image_attachment:
clean_for_history = f"{main_reply}\n{afterthought}" if afterthought else main_reply
self._chat_history[ch_id].append( self._chat_history[ch_id].append(
{"role": "assistant", "content": response} {"role": "assistant", "content": clean_for_history}
) )
# Reset proactive cooldown counter for this channel # Reset proactive cooldown counter for this channel
@@ -415,17 +433,6 @@ class ChatCog(commands.Cog):
except (asyncio.TimeoutError, asyncio.CancelledError): except (asyncio.TimeoutError, asyncio.CancelledError):
pass pass
# Split afterthoughts (triple-pipe delimiter)
main_reply = response
afterthought = None
if "|||" in response:
parts = response.split("|||", 1)
main_reply = parts[0].strip()
afterthought = parts[1].strip() if len(parts) > 1 else None
if not main_reply:
main_reply = response
afterthought = None
await message.reply(main_reply, mention_author=False) await message.reply(main_reply, mention_author=False)
if afterthought: if afterthought:
@@ -446,7 +453,7 @@ class ChatCog(commands.Cog):
reply_type.capitalize(), reply_type.capitalize(),
message.channel.name, message.channel.name,
message.author.display_name, message.author.display_name,
response[:100], main_reply[:100],
) )
@@ -518,15 +525,22 @@ class ChatCog(commands.Cog):
if not response: if not response:
return return
self._chat_history[ch_id].append({"role": "assistant", "content": response}) main_reply, afterthought = self._split_afterthought(response)
clean_for_history = f"{main_reply}\n{afterthought}" if afterthought else main_reply
self._chat_history[ch_id].append({"role": "assistant", "content": clean_for_history})
await channel.send(main_reply)
if afterthought:
await asyncio.sleep(random.uniform(2.0, 5.0))
await channel.send(afterthought)
await channel.send(response)
logger.info( logger.info(
"Reaction reply in #%s to %s (%s): %s", "Reaction reply in #%s to %s (%s): %s",
channel.name, channel.name,
member.display_name, member.display_name,
emoji, emoji,
response[:100], main_reply[:100],
) )