/ gateway / sticker_cache.py
sticker_cache.py
  1  """
  2  Sticker description cache for Telegram.
  3  
  4  When users send stickers, we describe them via the vision tool and cache
  5  the descriptions keyed by file_unique_id so we don't re-analyze the same
  6  sticker image on every send. Descriptions are concise (1-2 sentences).
  7  
  8  Cache location: ~/.hermes/sticker_cache.json
  9  """
 10  
 11  import json
 12  import time
 13  from typing import Optional
 14  
 15  from hermes_cli.config import get_hermes_home
 16  
 17  
 18  CACHE_PATH = get_hermes_home() / "sticker_cache.json"
 19  
 20  # Vision prompt for describing stickers -- kept concise to save tokens
 21  STICKER_VISION_PROMPT = (
 22      "Describe this sticker in 1-2 sentences. Focus on what it depicts -- "
 23      "character, action, emotion. Be concise and objective."
 24  )
 25  
 26  
 27  def _load_cache() -> dict:
 28      """Load the sticker cache from disk."""
 29      if CACHE_PATH.exists():
 30          try:
 31              return json.loads(CACHE_PATH.read_text(encoding="utf-8"))
 32          except (json.JSONDecodeError, OSError):
 33              return {}
 34      return {}
 35  
 36  
 37  def _save_cache(cache: dict) -> None:
 38      """Save the sticker cache to disk."""
 39      CACHE_PATH.parent.mkdir(parents=True, exist_ok=True)
 40      CACHE_PATH.write_text(
 41          json.dumps(cache, indent=2, ensure_ascii=False),
 42          encoding="utf-8",
 43      )
 44  
 45  
 46  def get_cached_description(file_unique_id: str) -> Optional[dict]:
 47      """
 48      Look up a cached sticker description.
 49  
 50      Returns:
 51          dict with keys {description, emoji, set_name, cached_at} or None.
 52      """
 53      cache = _load_cache()
 54      return cache.get(file_unique_id)
 55  
 56  
 57  def cache_sticker_description(
 58      file_unique_id: str,
 59      description: str,
 60      emoji: str = "",
 61      set_name: str = "",
 62  ) -> None:
 63      """
 64      Store a sticker description in the cache.
 65  
 66      Args:
 67          file_unique_id: Telegram's stable sticker identifier.
 68          description:    Vision-generated description text.
 69          emoji:          Associated emoji (e.g. "😀").
 70          set_name:       Sticker set name if available.
 71      """
 72      cache = _load_cache()
 73      cache[file_unique_id] = {
 74          "description": description,
 75          "emoji": emoji,
 76          "set_name": set_name,
 77          "cached_at": time.time(),
 78      }
 79      _save_cache(cache)
 80  
 81  
 82  def build_sticker_injection(
 83      description: str,
 84      emoji: str = "",
 85      set_name: str = "",
 86  ) -> str:
 87      """
 88      Build the warm-style injection text for a sticker description.
 89  
 90      Returns a string like:
 91        [The user sent a sticker 😀 from "MyPack"~ It shows: "A cat waving" (=^.w.^=)]
 92      """
 93      context = ""
 94      if set_name and emoji:
 95          context = f" {emoji} from \"{set_name}\""
 96      elif emoji:
 97          context = f" {emoji}"
 98  
 99      return f"[The user sent a sticker{context}~ It shows: \"{description}\" (=^.w.^=)]"
100  
101  
102  def build_animated_sticker_injection(emoji: str = "") -> str:
103      """
104      Build injection text for animated/video stickers we can't analyze.
105      """
106      if emoji:
107          return (
108              f"[The user sent an animated sticker {emoji}~ "
109              f"I can't see animated ones yet, but the emoji suggests: {emoji}]"
110          )
111      return "[The user sent an animated sticker~ I can't see animated ones yet]"