Die Inferenz-Runtime
API
Endpunkte, Struktur von Anfragen und Antworten, Statuscodes, CORS
Diese Seite wurde maschinell aus der englischen Dokumentation übersetzt.
Ein relevanter Endpunkt, aufgebaut wie bei OpenAI:
POST /v1/chat/completions Authorization: Bearer <key>, optional Accept-Language
GET /v1/models Authorization: Bearer <key>
GET /readyz no auth, can it serve a scan right now?
GET /healthz no auth, is the process alive?Sende einen Textteil und eine image_url-Daten-URI, genau wie an OpenAI. Die Modellkennung ist openplate-plate-1. choices[0].message.content ist sauberes JSON ohne Markdown-Formatierung:
{
"foods": [
{ "name": "scrambled eggs", "estimatedGrams": 80, "confidence": "high",
"portionHint": "a small scoop",
"macrosPer100g": { "carbs": 1.2, "protein": 10, "fat": 10, "kcal": 140 },
"translations": { "en": "scrambled eggs", "de": "Rührei" } }
],
"notes": "…"
}Dieser Dienst setzt provenance in einem Lebensmitteleintrag auf "corpus", wenn die Lebensmitteldatenbank dessen Makronährstoffe liefert. Er fügt einen attribution-String hinzu, wenn die Quelle dies erfordert, siehe Lebensmitteldaten. Wenn nichts den Eintrag auflöst, werden beide Felder weggelassen und macrosPer100g ist null. Dieser Dienst gibt niemals "model" aus, da der gemeinsame Vertrag diesen Wert für einen Cloud-Anbieter reserviert. Ein Quellenfehler, wie ein Timeout, eine Verweigerung oder ein Netzwerkfehler, sieht genauso aus wie kein Treffer: macrosPer100g ist null, die Antwort ist weiterhin 200, und nichts in der Antwort kennzeichnet, was davon eingetreten ist.
Dieser Dienst setzt flags (Allergene und Schwangerschaftskategorien) bei einem Lebensmittel, wenn er den Lebensmittelnamen erkennt, und setzt daneben flagsCoverage auf "partial". Die Flags stammen aus einer festen Wortliste im Code, nicht aus dem Modell. Ein Name kann zeigen, was ein Lebensmittel enthält, aber nicht, was ihm fehlt. Betrachte die Listen daher als Ausgangspunkt, nicht als Prüfung: Ein Allergen, das darin fehlt, kann dennoch im Lebensmittel enthalten sein. Wenn der Dienst den Namen nicht erkennt, lässt er beide Felder weg. Ein fehlendes flags bedeutet, dass das Lebensmittel nicht bewertet wurde, niemals, dass es sicher ist.
Dieser Dienst übersetzt Lebensmittelnamen in die im Request-Header Accept-Language angegebene Sprache, eine Sprache pro Request. Er liest den am höchsten gewichteten Tag, dessen Sprache zu den App-Sprachen (en, de, fr, it, es, tr) gehört, und ignoriert die Region, sodass de-DE Deutsch bedeutet. Wenn diese Sprache nicht Englisch ist, erhält jedes Lebensmittel ein translations-Objekt mit zwei Schlüsseln: dem englischen Namen und dem Namen in dieser Sprache. Das obige Beispiel ist ein mit Accept-Language: de gesendeter Request. name bleibt immer Englisch, da die Lebensmitteldatenbank damit sucht. Die Übersetzung ist ein zweiter, reiner Textaufruf an dasselbe Modell. Wenn er fehlschlägt oder länger als 20 Sekunden dauert, lässt der Dienst die Namen unverändert: Kein Lebensmittel in dieser Antwort hat translations, und die Antwort ist weiterhin 200. Ohne den Header, oder bei Englisch, * oder einer Sprache außerhalb dieser Liste, führt der Dienst keinen zweiten Aufruf durch und sendet kein translations.
Der Dienst nimmt ein Bild an und beantwortet eine Frage. Dein Prompt wird für das Bild gelesen und ansonsten verworfen.
Funktionen
GET /v1/models listet das eine Modell auf. Dessen Eintrag enthält ein capabilities-Objekt, das einem Client mitteilt, was dieser Dienst kann und was nicht. Lies es, bevor du einen Request sendest. Die anderen Schlüssel des Eintrags (id, object, created, owned_by) entsprechen dem OpenAI-Format und ändern sich nicht. Ein OpenAI-Client ignoriert den zusätzlichen Schlüssel.
{
"object": "list",
"data": [
{
"id": "openplate-plate-1",
"object": "model",
"created": 0,
"owned_by": "openplate",
"capabilities": {
"tasks": {
"plateImage": true,
"describe": false,
"pantryImage": false,
"pantryText": false,
"recipes": false
},
"flags": "partial",
"translations": "request-language",
"labels": false
}
}
]
}| Schlüssel | Werte | Bedeutung |
|---|---|---|
tasks.plateImage | true, false | Lebensmittel auf einem Teller anhand eines Fotos erkennen. true heute. |
tasks.describe | true, false | Eine getippte Beschreibung einer Mahlzeit in Lebensmittel umwandeln. |
tasks.pantryImage | true, false | Einen Vorratsartikel anhand eines Fotos erfassen. |
tasks.pantryText | true, false | Einen Vorratsartikel aus getipptem Text erfassen. |
tasks.recipes | true, false | Rezepte vorschlagen. |
flags | none, partial, complete | Wie viele Warn-Flags der Dienst bei jedem Lebensmittel setzt. Bei none bedeutet eine leere Flag-Liste nicht, dass ein Lebensmittel sicher ist. Dieser Dienst ist partial: Er listet auf, was er anhand des Lebensmittelnamens erkennen kann, und ein fehlendes flags bei einem Lebensmittel bedeutet, dass es nicht bewertet wurde. |
translations | none, request-language, all | In welchen Sprachen der Dienst Lebensmittelnamen zurückgibt. none ist eine Sprache, request-language ist die im Request angeforderte Sprache, all sind alle unterstützten Sprachen. Dieser Dienst ist request-language: Er liest Accept-Language, und ein Lebensmittel ohne translations hat nur sein englisches name. |
labels | true, false | Ob der Dienst eine gedruckte Nährwerttabelle auf einer Packung liest und deren Zahlen als macroSource: "label" zurückgibt. |
Die Menge der Schlüssel kann wachsen. Ein Client sollte einen fehlenden Schlüssel als false oder none behandeln.
Statuscodes
| Code | Bedeutung |
|---|---|
200 | Scan abgeschlossen. |
400 | Ungültiger Anfragekörper: Die Fehlermeldung nennt das Feld. |
401 | Fehlender oder falscher Bearer-Key. |
413 | Bild-Payload überschreitet das zulässige Limit. |
429 | Warteschlange voll (MAX_QUEUE_DEPTH) oder über RATE_LIMIT_RPM. Ein Retry-After-Header ist gesetzt. |
502 | Die Modell-Laufzeitumgebung ist nicht erreichbar, fehlgeschlagen oder setzt das JSON-Schema nicht durch. |
503 | Zugriff verweigert, da der Request nicht innerhalb von LATENCY_CEILING_MS abgeschlossen werden kann (nur wenn diese Obergrenze aktiviert ist). |
CORS
CORS ist bewusst vollständig geöffnet (*): Der Browser ruft diesen Endpunkt direkt auf. Eine Origin-Freigabeliste würde also bedeuten, dass alle Self-Hoster ihre Serverkonfiguration anpassen müssten. Das Fehlen impliziter Anmeldedaten macht diesen Ansatz sicher: Dieser Dienst setzt und liest keine Cookies. Eine fremde Seite kann zwar einen Cross-Origin-Request ausführen, erhält aber 401, da der Browser ihr keine Anmeldedaten anhängen kann.
Readiness
/readyz gibt nur dann 200 zurück, wenn ein Scan auch tatsächlich ausgeführt werden kann: Gewichte vorhanden, Modell geladen, Laufzeitumgebung antwortet. /healthz bedeutet lediglich, dass der Prozess läuft. Im externen Modus hat /readyz Grenzen, die du kennen solltest; siehe Readiness.
Warum es keine Admin-API und kein CLI gibt
Die beiden Schwesterdienste erhielten im August 2026 jeweils eine: openplate-gateway besitzt gw-api für seine Endpunkte rund um Mitglieder und Einladungen, und openplate-core hat sync-api für Account-Metadaten. Dieser Dienst verzichtet bewusst auf beides, und der Grund dafür gehört dokumentiert, damit dieses Fehlen als bewusste Entscheidung und nicht als Versäumnis verstanden wird.
Dieser Dienst implementiert die Spezifikation eines Dritten. Die Schnittstelle entspricht der Chat-Completions-Struktur von OpenAI, wodurch sich jeder OpenAI-kompatible Client, openplate eingeschlossen, ohne Adapter anbinden lässt. Eine API hat hier im striktesten Sinne Vorrang: Es gibt nichts außer der API, und ihr Format können wir nicht eigenmächtig erweitern.
Es gibt keinen administrativen Zustand zu verwalten. Ein Gateway verwaltet Mitglieder, Einladungen und Quotas, die alle länger existieren als ein einzelner Request und die man auflisten, widerrufen und prüfen muss. Ein Sync-Server verwaltet Konten. Dieser Dienst hier hat ein Modell, eine Warteschlange und eine Ratenbegrenzung. Jeder dieser Teile ist entweder eine Konfiguration, die beim Start eingelesen wird, oder ein Zustand, der mit dem Prozess endet. /readyz beantwortet bereits die einzige relevante Betriebsfrage (kann er jetzt gerade einen Scan verarbeiten) und tut dies ohne Zugangsdaten, genau wie es ein Monitoring-Probe benötigt.
Hier eine Admin-Schnittstelle zu entwerfen, würde bedeuten, Zustand zu erfinden, um sie zu rechtfertigen. Die Skripte in scripts/ sind Werkzeuge für Build, Gewichte-Download und Rauchtests: Sie dienen der Bedienbarkeit des Containers und sind keine Funktionen, die vor der API verborgen werden.
Sollte dieser Dienst jemals dauerhaften Zustand pro Aufrufer erhalten (Kontingente pro Schlüssel, ein Nutzungsprotokoll oder alles, was aufgelistet oder widerrufen werden muss), sollte diese Entscheidung überdacht werden. Das ist der Auslöser, auf den du achten musst.