Salta al contenuto
openplate

Il runtime di inferenza

API

Endpoint, struttura di richieste e risposte, codici di stato, CORS

Questa pagina è tradotta automaticamente dalla documentazione in inglese.

Un solo endpoint rilevante, con la stessa struttura di 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?

Invia una parte di testo e un URI di dati image_url, esattamente come faresti con OpenAI. L'identificatore del modello è openplate-plate-1. choices[0].message.content è JSON puro, senza blocchi di codice:

json
{
  "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": "…"
}

Questo servizio imposta provenance su "corpus" in un record alimentare quando il database degli alimenti ne fornisce i macronutrienti. Aggiunge una stringa attribution quando la fonte ne richiede una, vedi Dati sugli alimenti. Quando nessun elemento risolve la voce, entrambi i campi vengono omessi e macrosPer100g è null. Questo servizio non emette mai "model", poiché il contratto condiviso riserva quel valore a un provider cloud. Un errore della fonte, come un timeout, un rifiuto o un errore di rete, appare identico a una mancata corrispondenza: macrosPer100g è null, la risposta è comunque 200, e nessun elemento nella risposta indica cosa si sia verificato.

Questo servizio imposta flags (allergeni e categorie per la gravidanza) su un alimento quando ne riconosce il nome, e vi affianca flagsCoverage impostato su "partial". I contrassegni provengono da un elenco fisso di parole nel codice, non dal modello. Il nome può indicare cosa contiene un alimento, ma non cosa non contiene. Considera quindi gli elenchi come un punto di partenza e non come una verifica, un allergene assente dall'elenco può comunque essere presente nell'alimento. Quando il servizio non riconosce il nome, omette entrambi i campi. L'assenza di flags significa che l'alimento non è stato valutato, mai che sia sicuro.

Questo servizio traduce i nomi degli alimenti nella lingua specificata nell'intestazione di richiesta Accept-Language, una lingua per richiesta. Legge il tag con il peso maggiore la cui lingua rientra tra quelle dell'app (en, de, fr, it, es, tr) e ignora la regione, quindi de-DE indica il tedesco. Quando la lingua non è l'inglese, ogni alimento riceve un oggetto translations con due chiavi, il nome in inglese e il nome in quella lingua. L'esempio sopra è una richiesta inviata con Accept-Language: de. name rimane sempre in inglese, perché il database degli alimenti effettua le ricerche usandolo. La traduzione consiste in una seconda chiamata, di solo testo, allo stesso modello. Se fallisce o richiede più di 20 secondi, il servizio non modifica i nomi, nessun alimento in quella risposta conterrà translations, e la risposta sarà comunque 200. Senza l'intestazione, oppure con l'inglese, * o una lingua non inclusa nell'elenco, il servizio non esegue la seconda chiamata e non invia alcun translations.

Il servizio accetta un'immagine e risponde a una sola domanda. Il tuo prompt viene letto per l'immagine e ignorato per il resto.

Funzionalità

GET /v1/models elenca il modello singolo. La sua voce contiene un oggetto capabilities, che comunica a un client cosa questo servizio può o non può fare. Leggilo prima di inviare una richiesta. Le altre chiavi della voce (id, object, created, owned_by) seguono il formato di OpenAI e non cambiano. Un client OpenAI ignora la chiave aggiuntiva.

json
{
  "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
      }
    }
  ]
}
chiavevalorisignificato
tasks.plateImagetrue, falseIdentifica gli alimenti in un piatto da una foto. true oggi.
tasks.describetrue, falseConverte la descrizione digitata di un pasto in alimenti.
tasks.pantryImagetrue, falseLegge un articolo della dispensa da una foto.
tasks.pantryTexttrue, falseLegge un articolo della dispensa da un testo digitato.
tasks.recipestrue, falseSuggerisce ricette.
flagsnone, partial, completeIl numero di contrassegni di avviso che il servizio compila per ogni alimento. Con none, un elenco di contrassegni vuoto non significa che l'alimento sia sicuro. Questo servizio è partial, elenca ciò che riesce a riconoscere dal nome dell'alimento, e l'assenza di flags su un alimento significa non valutato.
translationsnone, request-language, allLe lingue in cui il servizio restituisce i nomi degli alimenti. none è una lingua sola, request-language è la lingua richiesta, all rappresenta tutte le lingue supportate. Questo servizio è request-language, legge Accept-Language, e un alimento privo di translations ha solo il suo name in inglese.
labelstrue, falseIndica se il servizio legge una tabella nutrizionale stampata su una confezione e ne restituisce i valori come macroSource: "label".

L'insieme delle chiavi può aumentare. Un client deve gestire una chiave mancante come false o none.

Codici di stato

codicesignificato
200Scansione completata.
400Corpo della richiesta non valido: il messaggio indica il campo.
401Chiave bearer mancante o errata.
413Payload dell'immagine superiore al limite consentito.
429Coda piena (MAX_QUEUE_DEPTH) o superiore a RATE_LIMIT_RPM. Viene impostata un'intestazione Retry-After.
502Il runtime del modello non è raggiungibile, ha generato un errore o non rispetta lo schema JSON.
503Richiesta rifiutata perché non può essere completata entro LATENCY_CEILING_MS (solo quando questo limite è attivo).

CORS

Il CORS è completamente aperto (*) per impostazione predefinita: il browser chiama direttamente questo endpoint, quindi una lista di origini consentite costringerebbe ogni persona che usa l'autohosting a modificare la configurazione del server. A rendere sicura questa scelta è l'assenza di credenziali implicite: questo servizio non genera cookie e non ne legge alcuno, quindi una pagina malevola può effettuare una richiesta cross-origin e ricevere un 401, poiché il browser non ha nulla da allegare automaticamente.

Stato di pronto

/readyz restituisce 200 solo quando una scansione può essere effettivamente eseguita: pesi presenti, modello caricato, runtime che risponde. /healthz indica solo che il processo è attivo. In modalità esterna /readyz presenta limiti utili da conoscere; vedi Stato di pronto.

Perché non ci sono API di amministrazione né CLI

I due servizi correlati ne hanno ricevuta una nell'agosto 2026: openplate-gateway ha gw-api per gli endpoint di membri e inviti, e openplate-core ha sync-api per i metadati degli account. Questo servizio è stato volutamente progettato senza nessuna delle due, e vale la pena metterne per iscritto il motivo, affinché l'assenza risulti una scelta e non una svista.

Questo servizio implementa la specifica di terzi. La sua interfaccia ricalca il formato chat-completions di OpenAI, consentendo a qualunque client compatibile con OpenAI, incluso openplate, di interfacciarsi senza alcun adattatore. L'API è qui prioritaria nel senso più assoluto: non esiste nient'altro oltre all'API, e il suo formato non spetta a noi estenderlo.

Non esiste alcuno stato amministrativo da gestire. Un gateway gestisce membri, inviti e quote, elementi che sopravvivono a una richiesta e devono essere elencati, revocati e controllati. Un server di sincronizzazione gestisce account. Questo servizio include solo un modello, una coda e un limitatore di frequenza, e ciascuno di essi è una configurazione letta all'avvio o uno stato che scompare con il processo. /readyz risponde già all'unica domanda operativa rilevante (può gestire una scansione adesso?) e lo fa senza credenziali, esattamente ciò di cui ha bisogno una sonda di monitoraggio.

Introdurre un'interfaccia di amministrazione significherebbe inventare lo stato necessario a giustificarla. Gli script in scripts/ servono per la compilazione, il recupero dei pesi e i test di base: sono strumenti operativi per la gestione del container, non funzionalità nascoste all'API.

Se questo servizio dovesse mai sviluppare uno stato persistente per chiamante (quote per chiave, un registro di utilizzo, qualsiasi cosa debba essere elencata o revocata), questa decisione andrà riesaminata, ed è questo l'evento da monitorare.

Modifica questa pagina su GitHub