Aller au contenu
openplate

Le moteur d'inférence

API

Points de terminaison, structure des requêtes et réponses, codes de statut, CORS

Cette page est traduite automatiquement à partir de la documentation en anglais.

Un seul point de terminaison compte, et il reprend la structure d'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?

Envoie une partie texte et un URI de données image_url, exactement comme tu le ferais vers OpenAI. L'identifiant du modèle est openplate-plate-1. choices[0].message.content est un JSON brut, sans balises :

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

Ce service définit provenance sur "corpus" dans un enregistrement d'aliment lorsque la base de données alimentaire fournit ses macros. Il ajoute une chaîne attribution quand la source en requiert une, voir Données alimentaires. Si rien ne résout l'élément, les deux champs sont omis et macrosPer100g est null. Ce service n'émet jamais "model", car le contrat partagé réserve cette valeur à un fournisseur cloud. Une défaillance de la source, comme un délai d'attente dépassé, un refus ou une erreur réseau, produit le même résultat qu'une absence de correspondance : macrosPer100g est null, la réponse reste 200, et rien dans la réponse n'indique ce qui s'est produit.

Ce service renseigne flags (allergènes et catégories pour femmes enceintes) sur un aliment quand il en reconnaît le nom, et définit flagsCoverage à "partial" à côté. Ces indicateurs proviennent d'une liste de mots codée en dur, pas du modèle. Un nom peut indiquer ce qu'un aliment contient, mais ne peut pas indiquer ce dont il est exempt. Considère donc ces listes comme un point de départ et non comme une vérification, un allergène absent de ces listes peut tout de même se trouver dans l'aliment. Lorsque le service ne reconnaît pas le nom, il omet les deux champs. L'absence de flags signifie que l'aliment n'a pas été évalué, jamais qu'il est sans danger.

Ce service traduit les noms d'aliments dans la langue indiquée dans l'en-tête de requête Accept-Language, à raison d'une langue par requête. Il lit l'étiquette au poids le plus élevé dont la langue fait partie de celles de l'application (en, de, fr, it, es, tr) et ignore la région, ainsi de-DE correspond à l'allemand. Quand cette langue n'est pas l'anglais, chaque aliment reçoit un objet translations avec deux clés : le nom en anglais et le nom dans cette langue. L'exemple ci-dessus correspond à une requête envoyée avec Accept-Language: de. name reste toujours en anglais, car la base de données d'aliments effectue ses recherches avec cette valeur. La traduction est un deuxième appel textuel au même modèle. Si elle échoue ou prend plus de 20 secondes, le service ne modifie pas les noms : aucun aliment dans cette réponse ne contient translations, et la réponse renvoie tout de même 200. Sans l'en-tête, ou avec l'anglais, * ou une langue absente de cette liste, le service n'effectue aucun second appel et n'envoie aucun translations.

Le service reçoit une seule image et répond à une seule question. Ton invite sert à interpréter l'image puis le service l'ignore.

Fonctionnalités

GET /v1/models liste l'unique modèle. Son entrée contient un objet capabilities, qui indique à un client ce que ce service sait ou ne sait pas faire. Lis-le avant d'envoyer une requête. Les autres clés de l'entrée (id, object, created, owned_by) correspondent au format d'OpenAI et ne changent pas. Un client OpenAI ignore la clé supplémentaire.

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
      }
    }
  ]
}
clévaleurssignification
tasks.plateImagetrue, falseIdentifier les aliments sur une assiette à partir d'une seule photo. true aujourd'hui.
tasks.describetrue, falseConvertir la description textuelle d'un repas en aliments.
tasks.pantryImagetrue, falseLire un article du garde-manger depuis une photo.
tasks.pantryTexttrue, falseLire un article du garde-manger depuis un texte saisi.
tasks.recipestrue, falseSuggérer des recettes.
flagsnone, partial, completeCombien d'indicateurs de prudence le service renseigne sur chaque aliment. Avec none, une liste d'indicateurs vide ne signifie pas que l'aliment est sans danger. Ce service est partial : il liste ce qu'il peut reconnaître d'après le nom de l'aliment, et l'absence de flags sur un aliment signifie qu'il n'a pas été évalué.
translationsnone, request-language, allDans quelles langues le service renvoie les noms d'aliments. none correspond à une seule langue, request-language est la langue demandée par la requête, all désigne toutes les langues prises en charge. Ce service est request-language : il lit Accept-Language, et un aliment sans translations ne dispose que de son name en anglais.
labelstrue, falseIndique si le service lit le tableau nutritionnel imprimé sur un emballage et renvoie ses valeurs sous forme de macroSource: "label".

L'ensemble des clés peut évoluer. Un client doit interpréter une clé manquante comme false ou none.

Codes d'état

codesignification
200Scan terminé.
400Corps de requête mal formé : le message indique le champ concerné.
401Clé bearer manquante ou incorrecte.
413Charge utile de l'image supérieure à la limite autorisée.
429File d'attente pleine (MAX_QUEUE_DEPTH) ou temps d'attente supérieur à RATE_LIMIT_RPM. Un en-tête Retry-After est défini.
502L'environnement d'exécution du modèle est inaccessible, en échec, ou n'impose pas le schéma JSON.
503Admission refusée car la requête ne peut pas aboutir dans la limite de LATENCY_CEILING_MS (uniquement si ce plafond est activé).

CORS

Le CORS est entièrement ouvert (*) à dessein : le navigateur appelle ce point de terminaison directement, donc une liste d'origines autorisées obligerait chaque auto-hébergeur à modifier sa configuration serveur. L'absence d'identifiants implicites garantit la sécurité : ce service ne crée ni ne lit aucun cookie, de sorte qu'une page malveillante peut émettre une requête cross-origin et recevoir un 401, car le navigateur n'a rien à joindre automatiquement.

État de disponibilité

/readyz renvoie 200 seulement si un scan peut réellement tourner : poids présents, modèle chargé, environnement d'exécution qui répond. /healthz indique simplement que le processus est vivant. En mode externe, /readyz comporte des limites utiles à connaître, voir État de disponibilité.

Pourquoi il n'y a ni API d'administration ni CLI

Les deux services frères en ont intégré une en août 2026 : openplate-gateway propose gw-api pour ses points de terminaison de membres et d'invitations, et openplate-core propose sync-api pour gérer les métadonnées de compte. Ce service n'en a délibérément reçu aucune, et il convient d'en détailler la raison pour que cette absence apparaisse comme un choix et non comme un oubli.

Ce service implémente la spécification d'un tiers. Son interface adopte le format chat-completions d'OpenAI, ce qui permet à n'importe quel client compatible OpenAI, y compris openplate, de s'y connecter sans adaptateur. L'API est ici « première » au sens le plus strict : il n'y a rien d'autre que l'API, et sa structure ne nous appartient pas.

Il n'y a aucun état administratif à gérer. Une passerelle gère des membres, des invitations et des quotas, qui survivent tous à une requête et doivent pouvoir être listés, révoqués et audités. Un serveur de synchronisation gère des comptes. Ce service possède un modèle, une file d'attente et un limiteur de débit, et chacun d'eux est soit une configuration lue au démarrage, soit un état qui disparaît avec le processus. /readyz répond déjà à la seule question opérationnelle qui compte (peut-il traiter une analyse en ce moment) et il y répond sans identifiant, ce qui correspond exactement au besoin d'une sonde de surveillance.

Créer une interface d'administration reviendrait à inventer l'état nécessaire pour la justifier. Les scripts dans scripts/ sont des outils de compilation, de récupération des poids et de tests rapides : ils simplifient la tâche de l'opérateur autour du conteneur, ce ne sont pas des fonctionnalités cachées de l'API.

Si ce service devait un jour conserver un état persistant par appelant (quotas par clé, registre d'utilisation, ou tout élément devant être listé ou révoqué), cette décision devrait être reconsidérée, et c'est le signal à surveiller.

Modifier cette page sur GitHub