image input pipeline: content-hash cache, data-url vision, picture_edit real

This commit is contained in:
Oleksandr Kozachuk
2026-07-13 16:33:48 +02:00
parent 7e6eae10ee
commit e21c262299
8 changed files with 496 additions and 10 deletions
+18 -6
View File
@@ -12,6 +12,7 @@ from pathlib import Path
from pprint import pformat
from typing import Any, Dict, List, Optional, Tuple, Union
from .images import ImageCache
from .memory import MemoryManager
from .persistence import PersistentStore
@@ -153,17 +154,16 @@ class AIResponder(AIResponderBase):
if stored_memory is not None:
self.memory = stored_memory
self.memory_manager = MemoryManager(self.store, lambda: self.config, self.consolidate, self.channel)
self.image_cache: Optional[ImageCache] = None
if self.store is not None:
self.image_cache = ImageCache(self.store, Path(self.config["history-directory"]).expanduser() / "images", lambda: self.config)
logging.info(f"memmory:\n{self.memory}")
# Dynamic values move to a context suffix so the persona prefix
# stays byte-stable for the prompt cache (ENV-20)
DYNAMIC_PLACEHOLDERS = ("{date}", "{time}", "{news}", "{memory}")
def message(self, message: AIMessage, limit: Optional[int] = None) -> List[Dict[str, Any]]:
messages = []
persona = self.config.get(self.channel, self.config["system"])
for placeholder in self.DYNAMIC_PLACEHOLDERS:
persona = persona.replace(placeholder, "")
def _context_lines(self, message: AIMessage) -> List[str]:
context = [f"date: {time.strftime('%Y-%m-%d')} ({time.strftime('%A')})", f"time: {time.strftime('%H:%M:%S')}"]
news_feed = self.config.get("news")
if news_feed and os.path.exists(news_feed):
@@ -173,7 +173,19 @@ class AIResponder(AIResponderBase):
memory_block = self.memory_manager.memory_block(participants, self.memory)
if memory_block:
context.append("memory:\n" + memory_block)
system = persona.rstrip() + "\n\n## Context\n" + "\n".join(context)
if self.image_cache is not None:
recent_images = self.image_cache.recent(message.channel, 4)
if recent_images:
# the model cannot use picture_edit unless told images exist (IMG-16)
context.append(f"recent images in this channel: {len(recent_images)} (set picture_edit=true to edit/remix the newest)")
return context
def message(self, message: AIMessage, limit: Optional[int] = None) -> List[Dict[str, Any]]:
messages = []
persona = self.config.get(self.channel, self.config["system"])
for placeholder in self.DYNAMIC_PLACEHOLDERS:
persona = persona.replace(placeholder, "")
system = persona.rstrip() + "\n\n## Context\n" + "\n".join(self._context_lines(message))
messages.append({"role": "system", "content": system})
if limit is not None:
while len(self.history) > limit: