Zum Inhalt

Grenzen

Was kaimarkit nicht kann und woran eine Umwandlung scheitert.

Fünf Werte begrenzen einen Aufruf

Alle fünf kommen aus der Umgebung. docs/admin/konfiguration.md beschreibt sie im Einzelnen, docker/.env.example nennt die Standardwerte.

Variable Standard Was sie begrenzt
KAIMARKIT_MAX_FILE_SIZE_MB 50 Größe einer einzelnen Datei
KAIMARKIT_MAX_FILES 20 Dateien je Stapelaufruf
KAIMARKIT_MAX_CONCURRENT 2 gleichzeitige Umwandlungen
KAIMARKIT_CONVERSION_TIMEOUT 600 Sekunden je Datei
KAIMARKIT_URL_TIMEOUT 30 Sekunden je Abruf über /api/convert/url

Die Größe prüft der Dienst schon beim Empfang. Überschreitet die Datei das Limit, bricht er ab und antwortet mit 413 und file_too_large. Den Rest des Uploads liest er nicht mehr ein — eine Prüfung danach käme zu spät, dann läge die Datei bereits vollständig im Speicher.

Solange eine Umwandlung läuft, liegt die Datei in einer temporären Datei. Danach löscht der Dienst sie, auch wenn die Engine gescheitert ist. Gespeichert wird nichts.

Die Zeitgrenze beendet den Wartevorgang, nicht die Engine

Dauert eine Umwandlung länger als KAIMARKIT_CONVERSION_TIMEOUT, antwortet der Dienst mit 504 und conversion_timeout. Die Engine arbeitet im Hintergrund weiter, bis sie von selbst fertig ist, und verbraucht so lange Rechenzeit. Häufen sich die Zeitüberschreitungen, sammeln sich diese Läufe an und der Dienst wird langsam; dann hilft nur ein Neustart des Containers.

Eine Ausnahme ist Pandoc: Es läuft als eigener Prozess, und den beendet der Dienst nach KAIMARKIT_PANDOC_TIMEOUT tatsächlich.

Wer regelmäßig an die Zeitgrenze stößt, setzt sie besser hoch, statt es mehrfach zu versuchen: Jeder Versuch legt einen weiteren Lauf obendrauf.

Gescannte Seiten ohne OCR bleiben leer

Ein PDF aus dem Scanner enthält Bilder und keinen Text. MarkItDown liest darin nichts, weil es nichts zu lesen findet, und Pandoc kommt für PDF ohnehin nicht in Frage. Das ist kein Fehler: Die Umwandlung gelingt, das Markdown bleibt leer, und die Antwort nennt den Grund in warnings.

Text aus solchen Dateien holt allein Docling, und nur mit eingeschalteter Texterkennung. KAIMARKIT_OCR_ENABLED setzt den Standard, das Feld ocr einer einzelnen Anfrage überschreibt ihn:

curl -sf -F file=@scan.pdf -F engine=docling -F ocr=true \
     localhost:8000/api/convert -o scan.md

Zwei Dinge kosten das. Die Texterkennung ist um ein Vielfaches langsamer als das Lesen einer Textebene, und ihre Sprachen müssen stimmen: KAIMARKIT_OCR_LANGS steht auf de,en, ein französisches Dokument braucht dort seinen eigenen Eintrag, fr.

Läuft der Aufruf mit engine=auto und ist Docling noch nicht bereit, nimmt der Dienst für das PDF die nächste Engine der Liste — und die findet dann eben keinen Text. Wer sicher OCR will, nennt Docling ausdrücklich und wartet.

OCR greift nur in PDF und Bilddateien

Wer ein Word-Dokument mit einem abfotografierten Absatz hochlädt, bekommt diesen Absatz nicht — auch mit ocr=true nicht. Die Texterkennung erreicht zwei Arten von Dateien: PDF und die Bildformate .png, .jpg, .jpeg und .tiff. In .docx, .pptx, .xlsx, .html und .epub bleibt sie aus.

Der Grund liegt in Docling. Ein PDF läuft dort durch eine eigene Pipeline, und nur diese baut das OCR-Modell; jedes andere Format geht an die einfache Pipeline, deren Optionen do_ocr gar nicht kennen. Es gibt also keinen Schalter, den man umlegen könnte: Weder das Feld ocr einer Anfrage noch KAIMARKIT_OCR_ENABLED wirkt auf diese Formate.

In PDF reicht die Texterkennung dafür bis in eingebettete Bilder hinein. Ein Absatz, der nur als Bild in der Seite steht, erscheint mit ocr=true im Markdown und fehlt mit ocr=false. Gemessen im Container-Abbild mit docling 2.124.0; eine spätere Version kann das ändern.

Der Umweg führt über PDF: das Dokument aus Word, LibreOffice oder dem Programm, aus dem es stammt, als PDF speichern und dieses PDF mit engine=docling und ocr=true abgeben.

Bilder werden nicht beschrieben

Ein Bild im Dokument erscheint im Markdown als Platzhalter oder als Alt-Text, nie als Beschreibung seines Inhalts. Der Dienst schickt bewusst nichts an ein Sprachmodell — er soll den Kontext zeigen, den man einem Modell gibt, und ihn nicht selbst erzeugen. Ein Diagramm, dessen Aussage nur im Bild steht, geht dabei verloren.

Bei einem PDF durch MarkItDown bleibt nicht einmal die Stelle übrig. Die Engine liest dort nur die Textebene und lässt jedes Bild ersatzlos weg; im Markdown steht danach weder Platzhalter noch Alt-Text. Der Dienst warnt deshalb bei jedem PDF, das durch MarkItDown läuft. Docling setzt stattdessen <!-- image --> ein und zählt die ersetzten Bilder.

Jeder Worker hält eigene Docling-Modelle

Die Modelle liegen im Speicher des Prozesses, nicht daneben. Zwei Uvicorn-Worker halten sie deshalb zweimal, mit jeweils rund 2 GB. KAIMARKIT_WORKERS erst hochsetzen, wenn genug RAM da ist, und KAIMARKIT_MEM_LIMIT mit anheben — sonst schießt der Kernel den Container beim ersten großen PDF ab. Dass es das war, zeigt docker inspect als OOMKilled.

Gleichzeitige Anfragen sind der falsche Grund für mehr Worker. Wie viele Umwandlungen nebeneinander laufen, regelt KAIMARKIT_MAX_CONCURRENT innerhalb eines Prozesses; das kostet keinen zweiten Satz Modelle.

Wartezeit kostet auch der erste Aufruf. Docling beginnt seine Modelle zu laden, sobald der Dienst hochfährt, und lädt sie im Hintergrund; jeder Neustart fängt damit von vorn an. Bis das fertig ist, meldet die Auskunft warming und engine=auto nimmt für ein PDF MarkItDown.

Webseiten: nur öffentlich, kein JavaScript

POST /api/convert/url holt genau eine Seite, und der Dienst prüft vorher, wohin er greift. Erlaubt sind http und https; jede andere Adresse endet mit 400 und invalid_url. Den Hostnamen löst er auf und prüft jede zurückgegebene Adresse: Loopback, private Netze, Link-local und alles andere, was nicht öffentlich erreichbar ist, weist er ab. Ein Name, der auf eine öffentliche und eine private Adresse zeigt, kommt ebenfalls nicht durch. http://127.0.0.1/ etwa endet mit 400 und invalid_url, und http://localhost:8000/ genauso — der Name schützt nicht davor, weil der Dienst ihn auflöst, bevor er verbindet. Der Grund steht im Betrieb: Der Dienst teilt sich sein Docker-Netz mit Traefik und Authelia, und ein ungeprüfter Abruf wäre ein Sprungbrett dorthin — bis zu 169.254.169.254, wo Cloud-Anbieter ihre Metadaten anbieten.

Einer Weiterleitung folgt der Dienst höchstens fünfmal, und er prüft das Ziel vor jedem Sprung erneut. Wer öfter weiterleitet, bekommt invalid_url.

Die geholte Antwort unterliegt derselben Größenprüfung wie ein Upload: Über KAIMARKIT_MAX_FILE_SIZE_MB bricht der Dienst mitten im Empfang ab und antwortet mit 413. Für den Abruf selbst gilt KAIMARKIT_URL_TIMEOUT — 30 Sekunden im Standard, alle Weiterleitungen eingeschlossen; danach 504. Die Umwandlung danach zählt wieder gegen KAIMARKIT_CONVERSION_TIMEOUT. Auch der Abruf belegt einen der KAIMARKIT_MAX_CONCURRENT Plätze: Wer zwanzig Adressen auf einmal schickt, öffnet nicht zwanzig Verbindungen zugleich.

Zwei Arten von Seiten liefern trotzdem nichts Brauchbares:

  • Seiten hinter einer Anmeldung. Der Dienst schickt keine Cookies, keine Kopfzeilen und keine Zugangsdaten mit. Er sieht, was ein fremder Browser ohne Sitzung sieht — meist die Anmeldeseite, und die wandelt er dann auch.
  • Seiten, die ihren Inhalt erst im Browser aufbauen. Es läuft kein JavaScript. Was der Server als HTML ausliefert, ist die Seite. Bei einer Anwendung, die ihren Text erst nachlädt, bleibt ein fast leeres Markdown übrig.

Die geholte Datei liegt wie ein Upload in einem eigenen temporären Verzeichnis, das nach der Antwort verschwindet — auch dann, wenn die Engine gescheitert ist.

Was der Dienst gar nicht tut

  • Nichts aufheben. Es gibt keine Historie und keinen Zwischenspeicher. Wer ein Ergebnis behalten will, lädt es herunter.
  • Niemanden erkennen. Die API kennt keine Anmeldung und keine Kennungen. Eine Anmeldung kommt von außen davor, siehe Authelia.
  • Nichts nachladen. Alle Modelle stecken im Abbild. Aus dem Netz holt der Dienst zur Laufzeit nur, was ein Aufruf von /api/convert/url verlangt: die eine Seite, von einer öffentlichen Adresse, und nichts von sich aus.
  • Nichts zurückschreiben. Der Weg führt nur in eine Richtung: nach Markdown. Aus Markdown wieder ein PDF zu machen, ist nicht Aufgabe dieses Dienstes.