feat(memory): Anhaenge in App-Bubble + System-Prompt (Stufe C + D)
Stufe C — App:
- ChatMessage.memorySaved.attachments [{name, mime, size, path, localUri}]
- memory_saved-Listener uebernimmt payload.attachments
- renderMessage memorySaved-Bubble zeigt Anhaenge als Tap-Reihen
(Icon 🖼/📄 + Filename + Hint). Tap → file_request via Bridge,
beim ersten Mal "(tippen zum Laden)" → nach file_response cached
+ bei Bildern setFullscreenImage, bei anderen openFileWithIntent
- file_response-Handler updated zusaetzlich memorySaved.attachments
per serverPath-Match
- Styles fuer memoryAttachmentRow/Icon/Name/Meta
Stufe D — System-Prompt:
- prompts._attachments_line: pro Memory eine Zeile
"📎 Anhaenge: foo.jpg (image/jpeg, 109 KB) — Pfad: /shared/memory-attachments/<id>/"
- Wird in build_hot_memory_section + build_cold_memory_section
nach dem Content angehangen
- ARIA "weiss" damit dass Anhaenge da sind und kann via Bash darauf
zugreifen (file, head, base64 …). Echt sehen kann sie sie erst mit
Multi-Modal-Pipeline (Stufe E)
- memory_save Dispatcher: attachments-Liste auch im memory_saved-Event
(vermutlich [] beim Save, aber konsistent fuer spaeteres
Speichern-mit-Anhaengen-Pattern)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -546,6 +546,7 @@ class Agent:
|
||||
"id": saved.id, "type": saved.type, "title": saved.title,
|
||||
"content_preview": (saved.content or "")[:140],
|
||||
"category": saved.category, "pinned": saved.pinned,
|
||||
"attachments": saved.attachments or [],
|
||||
},
|
||||
})
|
||||
return (f"OK — Memory '{title}' gespeichert "
|
||||
|
||||
@@ -52,6 +52,29 @@ TYPE_HEADINGS = {
|
||||
}
|
||||
|
||||
|
||||
def _attachments_line(p: MemoryPoint) -> str:
|
||||
"""Eine Zeile die ARIA verraet welche Dateien an einer Memory haengen.
|
||||
Bilder/Files liegen physisch unter /shared/memory-attachments/<id>/<name>
|
||||
— ARIA kann sie via Bash anschauen (file, head, base64...) wenn relevant.
|
||||
Sehen kann sie sie erst mit der Multi-Modal-Pipeline (Stufe E)."""
|
||||
atts = getattr(p, "attachments", None) or []
|
||||
if not atts:
|
||||
return ""
|
||||
items = []
|
||||
for a in atts:
|
||||
if not isinstance(a, dict):
|
||||
continue
|
||||
name = a.get("name", "?")
|
||||
mime = a.get("mime", "")
|
||||
size = a.get("size")
|
||||
size_part = f", {size // 1024} KB" if isinstance(size, int) and size else ""
|
||||
items.append(f"{name} ({mime}{size_part})")
|
||||
if not items:
|
||||
return ""
|
||||
base_dir = f"/shared/memory-attachments/{p.id}/" if p.id else ""
|
||||
return f"📎 Anhaenge: {', '.join(items)}" + (f" — Pfad: {base_dir}" if base_dir else "")
|
||||
|
||||
|
||||
def build_hot_memory_section(pinned: List[MemoryPoint]) -> str:
|
||||
"""Baue den 'IMMER-im-Prompt'-Block aus pinned Punkten."""
|
||||
grouped: dict[str, List[MemoryPoint]] = {}
|
||||
@@ -69,6 +92,9 @@ def build_hot_memory_section(pinned: List[MemoryPoint]) -> str:
|
||||
for p in items:
|
||||
parts.append(f"### {p.title}")
|
||||
parts.append(p.content.strip())
|
||||
att_line = _attachments_line(p)
|
||||
if att_line:
|
||||
parts.append(att_line)
|
||||
parts.append("")
|
||||
|
||||
# uebrige Types (falls jemand was anderes als pinned markiert)
|
||||
@@ -77,6 +103,9 @@ def build_hot_memory_section(pinned: List[MemoryPoint]) -> str:
|
||||
for p in items:
|
||||
parts.append(f"### {p.title}")
|
||||
parts.append(p.content.strip())
|
||||
att_line = _attachments_line(p)
|
||||
if att_line:
|
||||
parts.append(att_line)
|
||||
parts.append("")
|
||||
|
||||
return "\n".join(parts).strip()
|
||||
@@ -91,6 +120,9 @@ def build_cold_memory_section(matches: List[MemoryPoint]) -> str:
|
||||
score = f" [score={p.score:.2f}]" if p.score is not None else ""
|
||||
lines.append(f"- **{p.title}**{score}")
|
||||
lines.append(f" {p.content.strip()}")
|
||||
att_line = _attachments_line(p)
|
||||
if att_line:
|
||||
lines.append(f" {att_line}")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user