feat: add user alias mapping for jealousy detection context
Adds user_aliases config section mapping Discord IDs to known nicknames. Aliases are anonymized and injected into LLM analysis context so it can recognize when someone name-drops another member (even absent ones). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -253,6 +253,38 @@ class SentimentCog(commands.Cog):
|
||||
"""Replace display name keys with anonymous keys in user notes map."""
|
||||
return {anon_map.get(name, name): notes for name, notes in user_notes_map.items()}
|
||||
|
||||
@staticmethod
|
||||
def _build_alias_context(
|
||||
messages: list[discord.Message],
|
||||
anon_map: dict[str, str],
|
||||
alias_config: dict,
|
||||
) -> str:
|
||||
"""Build anonymized alias context string for the LLM.
|
||||
|
||||
Maps user IDs from messages to their known nicknames using the
|
||||
config, then replaces display names with anonymous keys.
|
||||
"""
|
||||
if not alias_config:
|
||||
return ""
|
||||
lines = []
|
||||
seen_ids: set[int] = set()
|
||||
for msg in messages:
|
||||
uid = msg.author.id
|
||||
if uid in seen_ids:
|
||||
continue
|
||||
seen_ids.add(uid)
|
||||
aliases = alias_config.get(uid)
|
||||
if aliases:
|
||||
anon_key = anon_map.get(msg.author.display_name, msg.author.display_name)
|
||||
lines.append(f" {anon_key} is also known as: {', '.join(aliases)}")
|
||||
# Also include aliases for members NOT in the conversation (so the LLM
|
||||
# can recognize name-drops of absent members)
|
||||
for uid, aliases in alias_config.items():
|
||||
uid = int(uid) if isinstance(uid, str) else uid
|
||||
if uid not in seen_ids:
|
||||
lines.append(f" (not in chat) also known as: {', '.join(aliases)}")
|
||||
return "\n".join(lines) if lines else ""
|
||||
|
||||
@staticmethod
|
||||
def _deanonymize_findings(result: dict, anon_map: dict[str, str]) -> None:
|
||||
"""Replace anonymous keys back to display names in LLM findings (in-place)."""
|
||||
@@ -466,6 +498,9 @@ class SentimentCog(commands.Cog):
|
||||
anon_conversation = self._anonymize_conversation(conversation, anon_map)
|
||||
anon_notes = self._anonymize_notes(user_notes_map, anon_map) if user_notes_map else user_notes_map
|
||||
|
||||
alias_config = config.get("user_aliases", {})
|
||||
alias_context = self._build_alias_context(all_messages, anon_map, alias_config)
|
||||
|
||||
channel_context = build_channel_context(ref_message, game_channels)
|
||||
|
||||
logger.info(
|
||||
@@ -480,6 +515,7 @@ class SentimentCog(commands.Cog):
|
||||
channel_context=channel_context,
|
||||
user_notes_map=anon_notes,
|
||||
new_message_start=new_message_start,
|
||||
user_aliases=alias_context,
|
||||
)
|
||||
|
||||
if result is None:
|
||||
@@ -499,6 +535,7 @@ class SentimentCog(commands.Cog):
|
||||
channel_context=channel_context,
|
||||
user_notes_map=anon_notes,
|
||||
new_message_start=new_message_start,
|
||||
user_aliases=alias_context,
|
||||
)
|
||||
if heavy_result is not None:
|
||||
logger.info(
|
||||
@@ -638,6 +675,9 @@ class SentimentCog(commands.Cog):
|
||||
anon_conversation = self._anonymize_conversation(conversation, anon_map)
|
||||
anon_notes = self._anonymize_notes(user_notes_map, anon_map) if user_notes_map else user_notes_map
|
||||
|
||||
alias_config = config.get("user_aliases", {})
|
||||
alias_context = self._build_alias_context(raw_messages, anon_map, alias_config)
|
||||
|
||||
channel_context = build_channel_context(raw_messages[0], game_channels)
|
||||
mention_context = (
|
||||
f"A user flagged this conversation and said: \"{mention_text}\"\n"
|
||||
@@ -650,6 +690,7 @@ class SentimentCog(commands.Cog):
|
||||
mention_context=mention_context,
|
||||
channel_context=channel_context,
|
||||
user_notes_map=anon_notes,
|
||||
user_aliases=alias_context,
|
||||
)
|
||||
|
||||
if result is None:
|
||||
|
||||
12
config.yaml
12
config.yaml
@@ -21,6 +21,13 @@ sentiment:
|
||||
escalation_threshold: 0.25 # Triage toxicity score that triggers re-analysis with heavy model
|
||||
escalation_boost: 0.04 # Per-message drama boost after warning (sustained toxicity ramps toward mute)
|
||||
|
||||
# Nicknames/aliases for server members. Used by the LLM to recognize
|
||||
# when someone references another member by name in chat.
|
||||
user_aliases:
|
||||
684222822272598058: ["Mark", "Limit"] # thelimitations
|
||||
1113144994790903908: ["Glam", "G"] # Glamgirlxx
|
||||
1195191381929508964: ["Bree"] # QueenBree10
|
||||
|
||||
game_channels:
|
||||
gta-online: "GTA Online"
|
||||
battlefield: "Battlefield"
|
||||
@@ -135,11 +142,6 @@ polls:
|
||||
duration_hours: 4
|
||||
cooldown_minutes: 60 # Per-channel cooldown between auto-polls
|
||||
|
||||
wordle:
|
||||
enabled: true
|
||||
bot_name: "Wordle" # Discord bot name to watch for
|
||||
reply_chance: 0.75 # Chance to comment on result summaries (0.0-1.0)
|
||||
playing_reply_chance: 0.0 # Chance to comment on "was playing" messages (0 = never)
|
||||
|
||||
coherence:
|
||||
enabled: true
|
||||
|
||||
@@ -489,6 +489,7 @@ class LLMClient:
|
||||
channel_context: str = "",
|
||||
user_notes_map: dict[str, str] | None = None,
|
||||
new_message_start: int | None = None,
|
||||
user_aliases: str = "",
|
||||
) -> dict | None:
|
||||
"""Analyze a conversation block in one call, returning per-user findings."""
|
||||
if not messages:
|
||||
@@ -497,6 +498,8 @@ class LLMClient:
|
||||
convo_block = self._format_conversation_block(messages, new_message_start=new_message_start)
|
||||
|
||||
user_content = f"=== CONVERSATION BLOCK ===\n{convo_block}\n\n"
|
||||
if user_aliases:
|
||||
user_content += f"=== KNOWN MEMBER ALIASES (names other members use to refer to each other) ===\n{user_aliases}\n\n"
|
||||
if user_notes_map:
|
||||
notes_lines = [f" {u}: {n}" for u, n in user_notes_map.items() if n]
|
||||
if notes_lines:
|
||||
|
||||
Reference in New Issue
Block a user