Deine iPhone-Fotos funktionieren endlich: HEIC-Dekodierung in vision_analyze – und Vision-Tools, die nicht mehr verschwinden

Du machst einen Screenshot von einem Fehler auf dem Handy, schickst ihn per AirDrop an die Maschine, auf der Hermes läuft, und bittest den Agenten, mal draufzuschauen. Er antwortet: source is not a recognized image. Die Datei ist da, du kannst sie lokal öffnen – aber für den Agenten ist sie überhaupt kein Bild. Zur gleichen Zeit passiert in einem lang laufenden Prozess etwas Seltsameres: vision_analyze funktionierte heute Morgen, und heute Nachmittag sagt es dir, Vision sei nicht verfügbar – und geändert hast du nichts.
Diese beiden Symptome stammen aus zwei unabhängigen Bugs, beide kürzlich behoben. Der eine ist ein Bildformat-Sniffer, der von iPhone-HEIC nie etwas gelernt hat; der andere eine Startup-Probe, die einen Client-Cache vergiftet hat und die Vision-Tools von selbst verschwinden ließ. Hier steht, was passiert ist – und wie du deine eigene Installation prüfst.
Symptom eins: Ein iPhone-Foto ist „kein Bild“
Zuerst die Kurzfassung des beteiligten Tools: vision_analyze ist die Art, wie der Agent ein Bild anschaut. Du gibst ihm einen Dateipfad oder einen Anhang, es schickt das Bild an ein Vision-Modell, und du bekommst eine Beschreibung oder eine Antwort zurück.
iPhone-Fotos sind standardmäßig HEIC (auch HEIF genannt), und das ist nicht dieselbe Familie wie JPEG. JPEG sind Pixel mit einem Header; HEIC steckt in einem Containerformat namens ISO-BMFF. Denk an den Container als Versandkarton, und die Marke auf dem Karton entscheidet, ob die Fracht ein HEVC-Standbild, ein AVIF-Bild oder ein Stück Video ist. Hermes entscheidet, ob eine Datei ein Bild ist – und welche Art –, indem es die ersten Bytes liest (Magic-Byte-Sniffing), statt der Dateiendung zu vertrauen, denn Upload-Pipelines beschriften HEIC routinemäßig als .jpg um. Endungen lügen, Header nicht.
Der alte Sniffer kannte nur die Visitenkarten von PNG / JPEG / GIF / WebP. Für ISO-BMFF-Container gab es überhaupt keinen Zweig. Also fiel eine HEIC-Datei aus dem Sniffer als „unbekannt“ heraus und erreichte nie den Re-Encode-Pfad, der SVG, BMP und TIFF längst verarbeitete. Das Bild war nicht nicht-konvertierbar. Es kam einfach nicht durch die Tür.
Fix eins: Die Brand aus der ftyp-Box lesen (PR #106408)
Der Fix ergänzt einen ISO-BMFF-ftyp-Zweig im Sniffer. Die Bytes 4 bis 8 des Containers sind immer ftyp, gefolgt von einer Major Brand und einer Liste kompatibler Brands – das Etikett der Box plus ihre „auch bekannt als“-Liste.
Drei Details in tools/vision_tools_image_prep.py sind wichtig:
- Sowohl die Major Brand als auch die Liste der kompatiblen Brands wird ausgewertet. AVIF deklariert routinemäßig das generische
mif1als Major Brand und identifiziert AV1 nur in der Kompatibilitätsliste. Eine Prüfung nur auf die Major Brand würde AVIF als unbekannt einstufen. - Der Scan ist durch die deklarierte Boxgröße begrenzt und schlägt bei einer fehlerhaften Größe geschlossen fehl. Dieses Längenfeld ist angreiferkontrollierte Eingabe; ließe man es den Scan in nachfolgende Boxen ausweiten, könnte das Sniff-Ergebnis konstruiert statt gelesen werden. Eine ungültige Größe ist ein Fehler, keine beste Vermutung.
- Andere
ftyp-Brands bleiben unerkannt.mp42(ein MP4-Video) ist kein Bild und wird nicht an ein Vision-Modell weitergereicht.
Sobald die Datei erkannt ist, nimmt sie den bestehenden Pfad _normalize_to_supported_image und wird vor dem Erreichen des Modells zu PNG konvertiert. Die HEIC-Dekodierung kommt von pillow-heif, das als soft dependency registriert ist – die vorgebauten Wheels bringen libheif mit, es sind also keine Systembibliotheken zu installieren. Ein fehlender Decoder ergibt eine umsetzbare Meldung statt eines kaputten Vision-Stacks.
AVIF wird bewusst etwas anders behandelt: Pillow 11.3 und später dekodiert AVIF nativ, während viele pillow-heif-Wheels überhaupt keinen AV1-Codec mitliefern – AVIF hängt also nicht an pillow-heif.
pillow-heif ist jetzt eine Core-Dependency (pillow-heif>=1.4.0,<2), eine frische Installation funktioniert also sofort:
# Fallback, falls du in einer abgespeckten Umgebung ohne Decoder läufst
pip install pillow-heif
# Hol die Version, die beide Fixes enthält
hermes update
Wissenswert: Das kam als Rettung eines Community-Beitrags (@samfoy) herein, und dieselbe Bugklasse wurde upstream in MoonshotAI/kimi-code behoben – deren ReadMediaFile verweigerte iPhone-.heic ebenfalls gegen eine hartcodierte PNG/JPEG/GIF/WebP-Allowlist ohne HEIF-Zweig. Dieselbe Schlussfolgerung auf beiden Seiten: Eine Format-Allowlist braucht Container-Sniffing, sonst scheitern „mit dem Handy aufgenommene Fotos“ dauerhaft.
Symptom zwei: Vision-Tools, die still verschwinden
Das zweite Symptom ist heimtückischer. In einem langlebigen Prozess – einem Gateway, einem Desktop-Backend, einer stundenlang offen gelassenen Sitzung – kann vision_analyze anfangen zu melden, Vision sei nicht verfügbar, und ein Neustart behebt es. Die naheliegenden Verdächtigen sind der Provider oder dein Kontingent. Der tatsächliche Übeltäter war eine Startup-Probe, die einen Stub-Client in den Runtime-Cache geschrieben hat.
Hier der Mechanismus. Beim Start führt Hermes eine Vision-Fähigkeitsprobe über den auxiliary model client aus. Damit der Netzwerk-Roundtrip dieser Probe nicht auf dem Startpfad liegt, nutzt die Probe einen Stub-Client und tauscht danach den echten ein. Der Bug: Der Stub wurde unter dem echten Runtime-Cache-Key abgelegt.
PR #110136 benennt die Konsequenz. Die nächste Probe trifft den gecachten Stub, erreicht _compat_model() und macht getattr(stub, "_client", None) – was RuntimeError auslöst, weil ein getattr-Default nur AttributeError abfängt. check_vision_requirements() verschluckte das zu False für den Rest der Prozesslaufzeit. Nur Modell-IDs mit Slash erreichen diesen Zweig, deshalb clusterten sich die Meldungen um vendor/model-Konfigurationen – NVIDIA, LM Studio, koboldcpp, litellm.
Der Fix hält Probe-Mode-Clients komplett aus dem Cache heraus und hängt den Guard daran, im Probe-Mode zu sein, statt am Typ des Objekts – denn Adapter-Wrapper (CodexAuxiliaryClient und Verwandte) sitzen um den Stub herum und vereiteln eine isinstance-Prüfung.
Ein ehrlicher Vorbehalt: Das heilt keinen bereits laufenden Prozess. Der Stub liegt im Cache dieses Prozesses; der Fix wirkt nach einem Neustart, den dir ein Upgrade ohnehin beschert.
Prüf es selbst
Rate nicht, in welchem Zustand deine Installation ist. Drei Kommandos:
# 1) Was die Datei wirklich ist
file ~/Downloads/IMG_1234.HEIC
# 2) Kann die Python-Seite sie dekodieren? (nachdem pillow-heif installiert ist)
python3 -c "import pillow_heif, PIL; pillow_heif.register_heif_opener(); im=PIL.Image.open('/path/to/IMG_1234.HEIC'); print(im.format, im.size)"
# 3) Frag den Agenten direkt, in einer Sitzung
# > use vision_analyze on /path/to/IMG_1234.HEIC and tell me what it shows
Wenn Schritt 2 „no module named pillow_heif“ wirft, fehlt dir der Decoder. Wenn Schritt 2 gelingt, Schritt 3 aber weiterhin sagt, die Quelle sei kein erkennbares Bild, läufst du mit ziemlicher Sicherheit Code von vor dem Fix – aktualisiere.
Wenn du viel mit Handy-Medien arbeitest, lohnen sich zwei verwandte Beiträge auf dieser Seite: sandboxed image and video delivery im Leitfaden zur Sandbox-Media-Auslieferung und Dokumentenextraktion für PDFs, Word, Excel und EPUB in read_file document extraction. Für Bilder, die schlicht zu klein sind, sieh dir den Media-Upscale-Leitfaden an.
Vorbehalte und Release-Status
- Normalisierung heißt Transcoding, nicht native Unterstützung. HEIC wird zu PNG, bevor es das Modell erreicht, eine Charge großer Fotos kostet also CPU und temporären Speicherplatz. Wirf nicht tausend Dateien auf einmal darauf.
- „Mein Desktop kann es öffnen“ beweist nichts. Dein Viewer bringt seinen eigenen Decoder mit; Hermes dekodiert serverseitig über einen anderen Pfad.
- Die beiden Fixes sind unabhängig. Format-Sniffing beantwortet „erkenne ich diese Datei“; der Probe-Cache beantwortet „will dieser Prozess noch Bilder anschauen“. Ein Upgrade trägt beide.
- Release-Status: Beide Änderungen (#106408, #110136) sind nach dem Tag v0.21.2 gemerged, liegen zum Zeitpunkt des Schreibens also auf
mainund noch in keinem getaggten Release.hermes updateholtmain, oder warte auf die nächste Version.
Das Fazit
Zwei Regeln decken beide Bugs ab: Sniffe den Container, nicht die Endung – und cache niemals einen Probe-Stub, als wäre er ein echter Client.
Im Alltag heißt das drei Dinge:
- iPhone- und Android-Fotos können jetzt direkt in
vision_analyze. - Wenn Vision nach dem Upgrade immer noch verweigert, prüfe, ob pillow-heif vorhanden ist.
- Wenn ein lang laufender Prozess plötzlich behauptet, Vision sei nicht verfügbar, starte ihn zuerst neu – und prüfe dann, ob du am alten Probe-Cache-Bug warst.
Wenn ein Agent dir das nächste Mal versichert, dein Bild sei kein Bild, weißt du genau, wo du nachschauen musst.