Referencia: eventos SSE¶
Inventario de los eventos Server-Sent Events (SSE) que emite el backend de Camarero IA. Esta página es material de referencia: describe cada canal, cada tipo de evento y la forma resumida de su payload, tal como están implementados.
Fuente de verdad
El contrato canónico vive en backend/tests/contract/sse_events_inventory.md
(snapshot exhaustivo bloqueado por tests de contrato). Esta página resume ese
inventario. Ante cualquier discrepancia, el inventario manda.
Términos:
- Canal / topic: cola lógica del bus de eventos a la que se suscribe un
cliente SSE. Hay dos relevantes para esta página:
session:{token}(cliente) ymanager:{restaurant_id}(dashboard del manager). - Frame: una línea
data: <json>\n\nenviada por el stream. - Envoltura: el JSON de cada frame del bus, con los campos
type,dataytimestamp.
Formato de envoltura¶
Los eventos publicados vía bus (EventService) llevan siempre esta envoltura:
| Campo | Tipo | Significado |
|---|---|---|
type |
string |
Identificador del evento (p.ej. cart_updated). Viaja dentro del JSON, no como cabecera SSE event:. |
data |
object |
Payload específico del evento. |
timestamp |
string (ISO-8601) |
Instante de emisión, UTC. |
El tipo NO viaja como cabecera SSE
A nivel de wire solo se usa el campo data:. Las cabeceras SSE event:,
id: y retry: no se emiten. El cliente debe leer type del JSON.
Cabeceras de respuesta de todos los streams SSE:
Content-Type: text/event-streamCache-Control: no-cacheConnection: keep-aliveX-Accel-Buffering: no
Canal cliente¶
GET /api/v1/sessions/{token}/events
Stream de la sesión de mesa. El cliente (ChatWidget) se suscribe al topic
session:{token} y recibe todo lo que ocurre en su sesión: chat, carrito,
comandas, llamadas al manager y eventos de ciclo de vida disparados por el
manager.
Mecanismo: pub/sub en memoria con buffer de los últimos eventos para
late-joiners; al timeout de cada ciclo (30 s) emite un heartbeat.
connected¶
Handshake inicial al conectar. Es el primer frame que recibe el cliente.
heartbeat¶
Keepalive emitido al expirar el ciclo de 30 s sin eventos.
cart_updated¶
El carrito ha cambiado (CRUD del cliente o modificación por la IA).
| Campo | Tipo | Nota |
|---|---|---|
items |
array |
Líneas del carrito. |
items[].id |
string (UUID) |
PK del item. |
items[].product_id |
integer |
FK al catálogo. |
items[].name |
string |
Nombre del producto. |
items[].price |
integer |
Precio unitario en céntimos. |
items[].quantity |
integer |
Cantidad. |
items[].extras |
array o null |
IDs de extras. |
items[].selected_options |
array o null |
Opciones estructuradas [{group_id, option_ids[]}]. |
items[].comment |
string o null |
Comentario del cliente. |
items[].image_url |
string o null |
URL de imagen. |
total |
integer |
Suma en céntimos. |
chat_message¶
Mensaje persistido en la conversación (usuario, asistente, saludo de apertura, mensaje proactivo o split-welcome).
Cinco variantes de payload
chat_message tiene cinco formas según el origen, con distintos sets de
claves. El cliente debe tratar todos los campos como opcionales. Núcleo común:
| Campo | Tipo | Nota |
|---|---|---|
role |
string |
user o assistant. Presente en todas las variantes. |
content |
string |
Texto del mensaje. Presente en todas las variantes. |
id / message_id |
integer |
ID del mensaje (ausentes en split-welcome). |
conversation_id |
integer |
ID de conversación (ausente en split-welcome). |
created_at |
string (ISO-8601) |
Timestamp (ausente en split-welcome). |
session_id |
integer |
Solo en variantes user/assistant. |
thinking_content |
string o null |
Solo en greeting y proactivo. |
message_type / extra_data |
varios | Solo en proactivo. |
Datos estructurados por mensaje
Las tarjetas de recomendación y los chips de respuesta rápida llegan en
extra_data.{recommendations, suggestions} de este evento (no solo en el frame
done del canal de chat), porque el chat_message del bus suele ganar la carrera
al crear el mensaje en el store del front.
typing_started / typing_stopped¶
La IA empieza o termina de "escribir". Se emiten al streamear respuestas, el
saludo de apertura o mensajes proactivos. typing_started también se reenvía
como replay si el cliente se conecta mientras la IA ya estaba escribiendo.
| Campo | Tipo | Nota |
|---|---|---|
conversation_id |
integer |
Conversación afectada. |
chat_streaming¶
Cada chunk de la respuesta de la IA durante el streaming por el bus.
| Campo | Tipo | Nota |
|---|---|---|
conversation_id |
integer |
Conversación. |
content |
string |
Texto acumulado (no incremental); el cliente calcula el diff. |
Eventos de comanda (batch_*)¶
Eventos del ciclo de vida de la comanda (Comanda / batch). Algunos los dispara
el cliente; otros, staff/admin, pero llegan igualmente al canal del cliente.
| Evento | Trigger | Payload resumido |
|---|---|---|
batch_updated |
Alta/edición/borrado de línea, aprobación, rechazo, etc. | Polimórfico: Forma A { "batch": {…} } (batch completo) o Forma B { "batch_id": int, "status": str }. Discriminar por presencia de claves. |
batch_ai_result |
Submit de comanda → validación IA | { "batch_id": int, "passed": bool, "validation_result": object } |
batch_manager_result |
Manager aprueba/rechaza | { "batch_id": int, "approved": bool } |
batch_approved |
Manager aprueba | { "batch_id": int } |
batch_rejected |
Manager rechaza | { "batch_id": int } |
batch_confirmed |
Staff confirma (pasa a cocina) | { "batch_id": int } |
batch_ready |
Staff marca como lista | { "batch_id": int } |
batch_served |
Staff sirve en mesa | { "batch_id": int } |
phase_updated |
Avance de fase al servir | { "phase": str } (valor de ConversationPhase) |
batch_updated es polimórfico
Tiene dos formas de payload. La Forma A incluye el batch completo (con lines[],
totales en céntimos, timestamps); la Forma B solo batch_id + status. Un cliente
debe comprobar si existe la clave batch antes de leer.
manager_call_created / manager_call_responded¶
Llamadas al manager desde la mesa y su respuesta.
manager_call_created (POST /sessions/{token}/call-manager):
| Campo | Tipo |
|---|---|
call_id |
integer |
session_token |
string |
table_id |
integer |
restaurant_id |
integer |
status |
string ("pending") |
reason |
string |
manager_call_responded (staff responde): añade status,
responded_by (integer) y responded_at (ISO-8601) al conjunto anterior.
session_closed¶
La sesión se cierra (cliente pide la cuenta o expira).
Otros eventos del canal cliente¶
| Evento | Trigger | Payload resumido |
|---|---|---|
guest_count_updated |
PATCH /sessions/{token}/guest-count |
{ "session_token": str, "guest_count": int } |
proactive_suggestion |
Scheduler de sugerencias | { "type": str, "message": str, "suggested_products": array } |
lifecycle_state_changed |
Acción de ciclo de vida del manager | { "state": str, "session_id": int } (ver canal manager) |
courtesy_round |
El manager invita una ronda | { "items": [str], "note": str } |
Canal manager¶
GET /api/v1/admin/manager/restaurants/{id}/events
Stream del dashboard de control de sala. Suscribe al topic
manager:{restaurant_id}, distinto del topic del cliente.
Autenticación por query param
EventSource no envía cabeceras, así que el JWT de admin viaja como
?token=... y se valida con require_restaurant_access. Mismo mecanismo de bus,
envoltura y cabeceras que el canal cliente.
sala_snapshot¶
Primer frame al conectar: estado inicial de todas las mesas activas.
| Campo | Tipo | Nota |
|---|---|---|
sessions |
array |
Lista de SessionSnapshot (forma de SessionSnapshotResponse del OpenAPI). |
table_state_changed¶
Transición de ciclo de vida de una mesa (entrega del manager, reconciliación de pedido, alerta de retraso).
| Campo | Tipo | Nota |
|---|---|---|
(todos los de SessionSnapshot) |
varios | Estado actual de la mesa. |
previous_state |
string |
Estado anterior. |
Se acompaña de lifecycle_state_changed
Cada table_state_changed del canal manager va acompañado de un
lifecycle_state_changed ({ "state", "session_id" }) publicado al canal del
cliente afectado, para que el chat reaccione (p.ej. a un DELAY_ALERT).
silent_table_alert¶
Tarea de fondo (cada 60 s): mesa en DRINKS_DELIVERED sin actividad > 15 min.
| Campo | Tipo |
|---|---|
session_id |
integer |
table_id |
integer |
session_token |
string |
minutes_silent |
integer |
message |
string |
Resumen de eventos¶
| Evento | Canal | Disparado por |
|---|---|---|
connected |
cliente | conexión |
heartbeat |
cliente / manager | timeout 30 s |
cart_updated |
cliente | CRUD carrito / IA |
chat_message |
cliente | mensaje persistido |
typing_started / typing_stopped |
cliente | streaming IA |
chat_streaming |
cliente | chunk de respuesta IA |
batch_updated |
cliente | ciclo de vida comanda |
batch_ai_result |
cliente | validación IA |
batch_manager_result |
cliente | manager aprueba/rechaza |
batch_approved / batch_rejected |
cliente | manager |
batch_confirmed / batch_ready / batch_served |
cliente | staff |
phase_updated |
cliente | servir comanda |
manager_call_created / manager_call_responded |
cliente | llamada al manager |
session_closed |
cliente | cierre / expiración |
guest_count_updated |
cliente | cambio de comensales |
proactive_suggestion |
cliente | scheduler |
lifecycle_state_changed |
cliente | acción del manager |
courtesy_round |
cliente | invitación del manager |
sala_snapshot |
manager | conexión |
table_state_changed |
manager | transición de mesa |
silent_table_alert |
manager | tarea de fondo |