feat(brain): memory_save mit attach_paths — ARIA haengt Bilder selbst an
Letzter Baustein vor Stefan's End-to-End-Test:
memory_attachments.attach_from_path(memory_id, src_path):
- Kopiert eine bestehende Datei aus /shared/uploads/ oder
/shared/memory-attachments/ in das Anhang-Verzeichnis der Memory
- Pfadschutz: nur ALLOWED_SOURCE_PREFIXES (/shared/uploads/,
/shared/memory-attachments/) — kein Zugriff auf Root-FS oder
SSH-Keys
- Groessen-Limit wie save_attachment (20 MB Default)
agent.py memory_save:
- Neuer optionaler Parameter `attach_paths: List[str]`
- Nach dem upsert: pro Pfad attach_from_path → Payload update mit
neuen Anhang-Metadaten
- Fehler beim Anhang sind nicht fatal (Memory bleibt gespeichert,
Hinweis in der Tool-Response)
- Tool-Description deutlich erweitert: expliziter Workflow-Hinweis
bei Bildern → erst `Read <pfad>` aufrufen (Claude Code Read ist
multi-modal), Texte/Kennungen/Marken in den content extrahieren,
dann erst memory_save mit attach_paths. Beispiel-Workflow als
Pseudocode mit Cessna 172 / Kennung D-EAAA.
End-to-End-Workflow ist jetzt einarmig moeglich:
User: "Ich hab eine Cessna 172" + Bild im Attachment
ARIA: Read /shared/uploads/aria_xy.jpg → sieht "Kennung D-EAAA"
ARIA: memory_save(content="Stefan besitzt eine Cessna 172,
Kennung D-EAAA, weiss/rot lackiert.",
attach_paths=["/shared/uploads/aria_xy.jpg"])
→ 🧠-Bubble mit Anhang in der App
→ Spaetere Frage "welche Kennung hat mein Flieger?" liefert via
Cold-Memory den Eintrag inkl. Kennung aus dem content
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -135,3 +135,38 @@ def read_bytes(memory_id: str, filename: str) -> Optional[bytes]:
|
||||
if not target.is_file():
|
||||
return None
|
||||
return target.read_bytes()
|
||||
|
||||
|
||||
# /shared/ ist der einzig akzeptable Source-Pfad fuer attach_from_path —
|
||||
# ARIA bekommt Files vom User immer in /shared/uploads, eigene Files
|
||||
# generiert sie in /shared/uploads/ als File-Marker. Kein Zugriff auf
|
||||
# /root, /etc, /tmp, ssh-Keys, etc.
|
||||
ALLOWED_SOURCE_PREFIXES = ("/shared/uploads/", "/shared/memory-attachments/")
|
||||
|
||||
|
||||
def attach_from_path(memory_id: str, source_path: str) -> dict:
|
||||
"""Kopiert eine existierende Datei aus /shared/* in das Anhang-Verzeichnis
|
||||
des Memories und gibt die neue Metadaten zurueck.
|
||||
|
||||
Verwendung: ARIA bekommt z.B. ein User-Bild als `/shared/uploads/aria_<id>.jpg`.
|
||||
Statt das Bild dort liegen zu lassen (kein direkter Memory-Bezug), kopiert
|
||||
sie es via `memory_save(..., attach_paths=[<src>])` ins Memory-Verzeichnis.
|
||||
|
||||
Pfadschutz: source_path MUSS unter /shared/ liegen — kein Zugriff auf
|
||||
Root-FS, SSH-Keys etc.
|
||||
"""
|
||||
if not memory_id:
|
||||
raise ValueError("memory_id ist Pflicht")
|
||||
if not source_path or not isinstance(source_path, str):
|
||||
raise ValueError("source_path leer")
|
||||
if not any(source_path.startswith(p) for p in ALLOWED_SOURCE_PREFIXES):
|
||||
raise ValueError(f"source_path muss unter {' oder '.join(ALLOWED_SOURCE_PREFIXES)} liegen")
|
||||
src = Path(source_path)
|
||||
if not src.is_file():
|
||||
raise ValueError(f"Datei nicht gefunden: {source_path}")
|
||||
size = src.stat().st_size
|
||||
if size > MAX_BYTES:
|
||||
raise ValueError(f"Datei zu gross ({size} > {MAX_BYTES} Byte)")
|
||||
# Reuse save_attachment damit Filename-Sanitization + Logging konsistent
|
||||
data = src.read_bytes()
|
||||
return save_attachment(memory_id, src.name, data)
|
||||
|
||||
Reference in New Issue
Block a user