El runtime de inferencia
API
Endpoints, estructura de petición y respuesta, códigos de estado, CORS
Esta página es una traducción automática de la documentación en inglés.
Solo hay un endpoint relevante, y sigue el formato de 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?Envía una parte de texto y un URI de datos image_url, exactamente como lo harías con OpenAI. El identificador del modelo es openplate-plate-1. choices[0].message.content es JSON limpio, sin bloques de código:
{
"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": "…"
}Este servicio establece provenance en "corpus" en el registro de un alimento cuando la base de datos de alimentos proporciona sus macros. Añade una cadena attribution cuando el origen lo requiere, consulta Datos de alimentos. Cuando nada resuelve el elemento, ambos campos se omiten y macrosPer100g queda como null. Este servicio nunca emite "model", ya que el contrato compartido reserva ese valor para un proveedor en la nube. Un fallo en el origen, como un tiempo de espera agotado, un rechazo o un error de red, se muestra igual que si no hubiera coincidencias: macrosPer100g es null, la respuesta sigue siendo 200 y nada en la respuesta indica qué ocurrió.
Este servicio asigna flags (alérgenos y categorías de embarazo) a un alimento cuando reconoce su nombre, y establece flagsCoverage en "partial" a su lado. Las etiquetas proceden de una lista fija de palabras en el código, no del modelo. Un nombre puede mostrar lo que contiene un alimento, pero no lo que le falta. Por tanto, toma las listas como un punto de partida y no como una comprobación: un alérgeno que no aparezca en ellas puede estar presente en el alimento. Cuando el servicio no reconoce el nombre, omite ambos campos. Si falta flags, significa que el alimento no se evaluó, nunca que sea seguro.
Este servicio traduce los nombres de los alimentos al idioma indicado en la cabecera de petición Accept-Language, un idioma por petición. Lee la etiqueta con mayor peso cuyo idioma sea uno de los de la aplicación (en, de, fr, it, es, tr) e ignora la región, por lo que de-DE significa alemán. Si ese idioma no es el inglés, cada alimento recibe un objeto translations con dos claves: el nombre en inglés y el nombre en ese idioma. El ejemplo anterior es una petición enviada con Accept-Language: de. name siempre se mantiene en inglés, ya que la base de datos de alimentos realiza las búsquedas con él. La traducción consiste en una segunda llamada, solo de texto, al mismo modelo. Si falla o tarda más de 20 segundos, el servicio deja los nombres tal cual: ningún alimento de esa respuesta incluye translations, y la respuesta sigue siendo 200. Sin la cabecera, o con inglés, * o un idioma fuera de esa lista, el servicio no realiza una segunda llamada ni envía translations.
El servicio acepta una imagen y responde una pregunta. Tu prompt se lee para interpretar la imagen y luego se descarta.
Capacidades
GET /v1/models lista el único modelo disponible. Su entrada incluye un objeto capabilities, que informa al cliente sobre lo que este servicio puede y no puede hacer. Léelo antes de enviar una petición. Las demás claves de la entrada (id, object, created, owned_by) corresponden a la estructura de OpenAI y no cambian. Un cliente de OpenAI ignora la clave adicional.
{
"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
}
}
]
}| clave | valores | significado |
|---|---|---|
tasks.plateImage | true, false | Identifica los alimentos de un plato a partir de una sola foto. true hoy. |
tasks.describe | true, false | Convierte la descripción escrita de una comida en alimentos. |
tasks.pantryImage | true, false | Lee un producto de la despensa a partir de una foto. |
tasks.pantryText | true, false | Lee un producto de la despensa a partir de texto escrito. |
tasks.recipes | true, false | Sugiere recetas. |
flags | none, partial, complete | Cuántas etiquetas de precaución completa el servicio para cada alimento. Con none, una lista de etiquetas vacía no significa que el alimento sea seguro. Este servicio es partial: lista lo que puede reconocer a partir del nombre del alimento, y la ausencia de flags en un alimento significa que no se evaluó. |
translations | none, request-language, all | En qué idiomas devuelve el servicio los nombres de los alimentos. none indica un único idioma, request-language indica el idioma solicitado en la petición y all abarca todos los idiomas compatibles. Este servicio es request-language: lee Accept-Language, y un alimento sin translations solo conserva su name en inglés. |
labels | true, false | Indica si el servicio lee la tabla nutricional impresa de un envase y devuelve sus valores como macroSource: "label". |
El conjunto de claves puede aumentar. Un cliente debe tratar cualquier clave ausente como false o none.
Códigos de estado
| código | significado |
|---|---|
200 | Análisis completado. |
400 | Cuerpo de la solicitud con formato incorrecto, el mensaje indica el campo. |
401 | Clave de autenticación bearer ausente o incorrecta. |
413 | Carga útil de la imagen superior al límite aceptado. |
429 | Cola llena (MAX_QUEUE_DEPTH) o por encima de RATE_LIMIT_RPM. Se incluye una cabecera Retry-After. |
502 | El runtime del modelo es inaccesible, falló o no aplica el esquema JSON. |
503 | Admisión rechazada porque la solicitud no puede terminar dentro de LATENCY_CEILING_MS (solo cuando este tope está activado). |
CORS
CORS está completamente abierto (*) por diseño: el navegador llama a este endpoint directamente, de modo que una lista de orígenes permitidos obligaría a cada usuario que se autoaloja el servicio a editar la configuración del servidor. Lo que hace que esto sea seguro es la ausencia de credenciales de sesión, ya que este servicio no emite cookies ni lee ninguna; por tanto, una página maliciosa puede hacer una solicitud de origen cruzado y obtener un 401, dado que el navegador no tiene nada que adjuntar de forma automática.
Estado de preparación
/readyz devuelve 200 solo cuando un escaneo realmente va a ejecutarse: pesos presentes, modelo cargado, runtime respondiendo. /healthz solo significa que el proceso está vivo. En modo externo, /readyz tiene límites que conviene conocer; consulta Estado de preparación.
Por qué no hay API de administración ni CLI
Los dos servicios hermanos incorporaron una en agosto de 2026: openplate-gateway tiene gw-api sobre sus endpoints de miembros e invitaciones, y openplate-core tiene sync-api sobre una superficie de metadatos de cuenta. Este servicio no incorporó ninguna de las dos de forma deliberada, y vale la pena dejar escrita la razón para que su ausencia se entienda como una decisión y no como un descuido.
Este servicio implementa la especificación de un tercero. Su superficie sigue el formato chat-completions de OpenAI, que es lo que permite que cualquier cliente compatible con OpenAI, incluido openplate, apunte hacia él sin necesidad de adaptadores. Aquí una API es prioritaria en el sentido más estricto: no existe nada más que la API, y su forma no nos corresponde ampliarla.
No hay estado administrativo que gestionar. Una pasarela tiene miembros, invitaciones y cuotas, elementos que sobreviven a una petición y necesitan listarse, revocarse y auditarse. Un servidor de sincronización tiene cuentas. Este servicio tiene un modelo, una cola y un limitador de tasa, y cada uno de ellos es o bien configuración leída al arrancar o bien estado que desaparece con el proceso. /readyz ya responde a la única pregunta operativa relevante (si puede procesar un escaneo ahora mismo) y lo hace sin credenciales, que es justo lo que necesita un sondeo de monitorización.
Inventar aquí una superficie de administración obligaría a inventar el estado que la justifique. Los scripts de scripts/ son herramientas de compilación, descarga de pesos y pruebas de humo: son comodidades de operación alrededor del contenedor, no funciones ocultas a la API.
Si este servicio llega a necesitar un estado persistente por cliente (cuotas por clave, un registro de uso o cualquier dato que deba listarse o revocarse), habrá que replantearse esta decisión, y ese será el detonante al que prestar atención.