- Random opening interjections (25% chance, configurable)- Pattern-based vocabulary replacement (Wow! → Tartar sauce!) - Preserves code blocks, URLs, file paths - Zero-token post-processing via transform_llm_output hook
167 lines
No EOL
5.3 KiB
Python
167 lines
No EOL
5.3 KiB
Python
"""
|
|
SpongeBob vocabulary transformer for BarnacleBoy.
|
|
|
|
Two modes:
|
|
1. Pattern-based vocabulary replacement (Wow! → Tartar sauce!)
|
|
2. Random opening interjections (Jumping jellyfish! Let's...)
|
|
|
|
Hook: transform_llm_output
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
import logging
|
|
import random
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Opening interjections - randomly prepended to responses
|
|
# These are BarnacleBoy's signature exclamations
|
|
OPENING_INTERJECTIONS = [
|
|
"Jumping jellyfish!",
|
|
"Tartar sauce!",
|
|
"Barnacles!",
|
|
"Holy Krabby Patties!",
|
|
"Great Neptune's nostrils!",
|
|
"Mother of Pearl!",
|
|
"Flappin' flounders!",
|
|
"Holy fish paste!",
|
|
"Holy shrimp!",
|
|
"Great Barrier Reef!",
|
|
"Aw, fishpaste!",
|
|
"Holy cephalopod!",
|
|
"What the barnacle!",
|
|
]
|
|
|
|
# Vocabulary replacements - case-insensitive regex patterns
|
|
# Format: (pattern, replacement)
|
|
VOCABULARY_PATTERNS = [
|
|
# Exclamations (surprise, excitement) - standalone or at start
|
|
(r'\bWow!\s', 'Tartar sauce! '),
|
|
(r'\bWow!\Z', 'Tartar sauce!'),
|
|
(r'\bWow,', 'Tartar sauce,'),
|
|
|
|
# Amazing variations
|
|
(r'\b[Aa]mazing!\s', 'Holy Krabby Patties! '),
|
|
(r'\b[Aa]mazing!\Z', 'Holy Krabby Patties!'),
|
|
|
|
# Excellent
|
|
(r'\b[Ee]xcellent!\s', 'Holy fish paste! '),
|
|
(r'\b[Ee]xcellent!\Z', 'Holy fish paste!'),
|
|
|
|
# Great
|
|
(r'\bGreat!\s', 'Barnacles! '),
|
|
(r'\bGreat!\Z', 'Barnacles!'),
|
|
|
|
# Perfect
|
|
(r'\b[Pp]erfect!\s', 'Holy cephalopod! '),
|
|
(r'\b[Pp]erfect!\Z', 'Holy cephalopod!'),
|
|
|
|
# Fantastic
|
|
(r'\b[Ff]antastic!\s', 'Holy shrimp! '),
|
|
(r'\b[Ff]antastic!\Z', 'Holy shrimp!'),
|
|
|
|
# Brilliant
|
|
(r'\bBrilliant!', 'Great Barrier Reef!'),
|
|
|
|
# Damn frustration
|
|
(r'\b[Dd]amn!', 'Barnacles!'),
|
|
(r'\b[Dd]amn,', 'Aw, barnacles,'),
|
|
|
|
# Ugh frustration
|
|
(r'\bUgh!', 'Fish paste!'),
|
|
(r'\bUgh,', 'Aw, fish paste!'),
|
|
|
|
# Fantastic praise
|
|
(r'\b[Ff]antastic!', 'Mother of Pearl!'),
|
|
|
|
# Common openers
|
|
(r'\bWell,', "Flappin' flounders,"),
|
|
(r'\bOh,', 'Oh, barnacles,'),
|
|
]
|
|
|
|
# Patterns to preserve (never transform)
|
|
PRESERVE_PATTERNS = [
|
|
r'```[\s\S]*?```', # Code blocks
|
|
r'`[^`]+`', # Inline code
|
|
r'https?://[^\s]+', # URLs
|
|
r'~/[^\s]+', # File paths starting with ~
|
|
r'/[a-zA-Z][^\s]*', # Absolute paths (start with /)
|
|
r'\$[^\n]+', # Shell commands
|
|
]
|
|
|
|
|
|
def transform_vocabulary(response_text: str, session_id: str = "", model: str = "", platform: str = "") -> str:
|
|
"""
|
|
Transform response text with SpongeBob vocabulary.
|
|
|
|
Args:
|
|
response_text: The LLM's output text
|
|
session_id: Session identifier (for context-aware decisions)
|
|
model: Model name (unused but required by hook signature)
|
|
platform: Platform name (e.g., "discord", "telegram")
|
|
|
|
Returns:
|
|
Transformed text with SpongeBob vocabulary, or None if disabled.
|
|
Non-None non-empty string takes precedence over other hooks.
|
|
"""
|
|
# Check if SpongeBob mode is enabled
|
|
mode = os.environ.get("SPONGEBOB_MODE", "").lower()
|
|
if mode not in ("on", "1", "true", "yes"):
|
|
return None # Not enabled, let other hooks or default pass through
|
|
|
|
if not response_text or not response_text.strip():
|
|
return None # Empty response, nothing to transform
|
|
|
|
logger.info("[spongebob-vocab] Hook fired, transforming output")
|
|
|
|
# Find all regions to preserve
|
|
preserved = []
|
|
preserved_text = response_text
|
|
|
|
for pattern in PRESERVE_PATTERNS:
|
|
for match in re.finditer(pattern, response_text, re.MULTILINE):
|
|
placeholder = f"\x00PRESERVE{len(preserved)}\x00"
|
|
preserved_text = preserved_text.replace(match.group(0), placeholder)
|
|
preserved.append(match.group(0))
|
|
|
|
# Apply vocabulary transformations
|
|
transformed = preserved_text
|
|
for pattern, replacement in VOCABULARY_PATTERNS:
|
|
transformed = re.sub(pattern, replacement, transformed)
|
|
|
|
# Restore preserved regions
|
|
for i, original in enumerate(preserved):
|
|
transformed = transformed.replace(f"\x00PRESERVE{i}\x00", original)
|
|
|
|
# Add random opening interjection (25% chance)
|
|
# Configurable via SPONGEBOB_INTERJECTION_CHANCE env var(0.0-1.0)
|
|
interjection_chance = float(os.environ.get("SPONGEBOB_INTERJECTION_CHANCE", "0.25"))
|
|
|
|
if random.random() < interjection_chance:
|
|
# Pick a random interjection
|
|
interjection = random.choice(OPENING_INTERJECTIONS)
|
|
|
|
# Check if response already starts with an interjection-like pattern
|
|
# (avoid double-interjecting if the LLM already started with one)
|
|
first_line = transformed.split('\n')[0].strip()
|
|
already_interjected = any(
|
|
first_line.startswith(exc.rstrip('!')) or first_line.startswith(exc)
|
|
for exc in OPENING_INTERJECTIONS[:6] # Check common ones
|
|
)
|
|
|
|
if not already_interjected:
|
|
# Prepend interjection with newline separator
|
|
transformed = f"{interjection} {transformed}"
|
|
logger.info(f"[spongebob-vocab] Prepended interjection: {interjection}")
|
|
|
|
return transformed
|
|
|
|
|
|
def register(ctx):
|
|
"""Register the transform hook with Hermes."""
|
|
ctx.register_hook("transform_llm_output", transform_vocabulary)
|
|
logger.info("[spongebob-vocab] Registered transform_llm_output hook")
|
|
|
|
|
|
__all__ = ["transform_vocabulary", "register"] |