Contrat du protocole SSE
POST /chat/control/stream répond au moyen des événements envoyés par le serveur (text/event-stream) : une réponse persistante qui transmet le texte au fur et à mesure de sa rédaction, ainsi que des événements structurés représentant les lieux, les actions cartographiques et les itinéraires.
Cette page définit le contrat de ce flux. Si vous utilisez kaleidr.js, le panneau de discussion lit le flux à votre place et vous n’avez pas besoin de cette page. Consultez-la lorsque vous créez votre propre client. Des clients opérationnels en Node et Python figurent dans Lecture du flux ; le corps de la requête est décrit dans Points de terminaison.
/chat/summary/stream et /chat/button/stream utilisent le même format de trame, avec un ensemble réduit d’événements. Ils sont décrits à la fin de cette page.
Avant le début du flux
Une requête refusée ne devient jamais un flux. Les échecs d’authentification, de portée d’accès, de quota, de limitation de débit, de validation ou de taille produisent une réponse HTTP ordinaire avec un corps JSON et un statut 4xx (Erreurs). Vérifiez d’abord le statut et interprétez le corps comme du SSE uniquement lorsqu’il vaut 200.
La requête est limitée à 400 messages, 64 000 caractères par message et 200 000 caractères pour l’ensemble des messages. Le dépassement de l’une de ces limites renvoie un code 422, par exemple conversation_too_long, tandis qu’un corps de plus de 2 Mio renvoie un 413. La liste complète figure dans Points de terminaison.
Une réponse réussie contient ces en-têtes :
HTTP/1.1 200 OK
Content-Type: text/event-stream; charset=utf-8
Cache-Control: no-cache, no-transform
Format des trames
Une trame comporte une ou deux lignes, suivies d’une ligne vide. Les lignes se terminent par \n.
event: place
data: {"name": "Louvre Museum", "coordinates": {"lat": 48.8606, "lng": 2.3376}}
data: {"text": "The Louvre sits on the right bank"}
: keep-alive
- Une trame nommée comporte une ligne
event:suivie d’une lignedata:. - Une trame de texte comporte uniquement une ligne
data:et aucune ligneevent:. - Un commentaire commence par deux-points. Kaleidr envoie
: keep-alivecomme signal de maintien de connexion. Il ne contient pas de données. data:contient toujours un objet JSON complet sur une seule ligne. Le texte est encodé en UTF-8.
Les fragments réseau ne correspondent pas nécessairement aux trames. Une seule lecture peut recevoir une demi-trame ou plusieurs. Mettez en mémoire tampon les données reçues, séparez-les aux sauts de ligne et ne traitez une trame qu’après avoir atteint sa ligne vide.
Règle de compatibilité
Ignorez les noms d’événements inconnus et les champs que vous n’utilisez pas. De nouveaux événements et champs peuvent être ajoutés sans changement de version. Un client qui échoue à la réception d’un événement inconnu ne fonctionnera plus.
Événements stables
event: | Charge utile | Signification |
|---|---|---|
| (sans nom) | { text } | Prochain fragment du texte de la réponse. Ajoutez-le au texte existant. Peut contenir des marqueurs [[Name]] autour des noms de lieux. |
phase | { type: "phase", phase: "thinking", session_id } | La requête est acceptée et le traitement a commencé. phase vaut toujours "thinking". |
start | { type: "start", session_id, purpose: "control" } | La transmission du texte va commencer. |
place | { name, coordinates?: { lat, lng }, address?, category?, icon?, source?, id?, unverified? } | Un lieu mentionné dans la réponse. Regroupez par name ; les premières coordonnées reçues pour un nom sont définitives. source: "vendor" désigne un lieu issu des données de votre organisation et source: "map" un lieu déjà visible sur la carte publiée. |
place_linked | champs du lieu avec is_update? | Détails supplémentaires d’un lieu déjà envoyé. Fusionnez-les avec le lieu portant le même name ; ignorez-les si vous ne l’avez pas reçu. |
early_actions | { type: "early_actions", actions: [ { type, payload } ], places_count, is_partial: true } | Actions cartographiques applicables avant la fin de la réponse, envoyées dès qu’au moins deux lieux ont des coordonnées. |
prompt_options | { type: "prompt_options", schema_version, kind, question, allow_free_text, options: [ { label, send_text } ] } | Question à choix envoyée lorsque la requête est ambiguë. Affichez les options et envoyez le send_text choisi comme prochain message utilisateur. |
route_clear | {} | Effacez les itinéraires tracés. De nouveaux itinéraires suivent. |
route | { type: "route", schema_version, id?, profile, origin, destination, primary, alternatives[], comparison[], stops?, color?, label?, transit?, attribution? } | Itinéraire calculé, envoyé avant le texte lors d’une question de guidage pour que la carte puisse se dessiner pendant la transmission. |
end | { type: "end", full_text, places[], actions[], question_type, response_time, total_tokens?, dropped_places? } | La réponse est terminée. Ce résultat fait autorité. |
error | { type: "error", message, code?, partial_text } | La réponse a échoué. Envoyé à la place de end. |
quota | { type: "quota", product, meter, tier, used, limit, reset_at, period } avec une clé de serveur ; { type: "quota", meter, reset_at } avec un jeton de session | Consommation de crédits IA de votre organisation après facturation de cet appel. Dernier événement du flux, après end ou error. Voir quota. |
: keep-alive | ligne de commentaire | Signal envoyé après 10 secondes de silence. Ignorez-le, mais comptabilisez-le comme une activité. |
Supprimé : grounding (2026-09-05). Il n’est plus envoyé.
Fiches et couches de données en direct
Une question portant sur des faits mesurés ou en direct peut ajouter l’un de ces événements. Une fiche arrive avant le texte ; une couche peut arriver pendant ou après. Ils sont complémentaires : affichez ceux que vous prenez en charge et ignorez les autres. D’autres champs pourront être ajoutés.
event: | Charge utile | Utilisation |
|---|---|---|
weather_card | objet contenant lat, lng, name, les températures (temp_c, temp_f), conditions et éventuellement forecast[] | Météo actuelle en un lieu. |
weather_layer | objet contenant layer, tile_path_template, tile_size, max_zoom, scope, attribution | Couche de température couvrant une région. |
satellite_card | objet contenant name, norad_id, lat, lng, altitude_km, velocity_kms, observed_at | Position d’un satellite nommé. |
constellation_layer | objet contenant group, features[] de { name, norad_id, lat, lng } et éventuellement track | Satellites voisins de celui présenté sur la fiche. |
building_card | objet contenant lat, lng, éventuellement name, height_m, num_floors | Données sur un bâtiment. |
trees_card | objet contenant lat, lng, count, radius_m | Arbres recensés autour d’un point. |
heritage_card | objet contenant lat, lng, count, sites[] | Sites patrimoniaux autour d’un point. |
rail_card | objet contenant lat, lng, lines[] et éventuellement nearest_station | Lignes ferroviaires proches d’un point. |
arrivals_card | objet contenant lat, lng, stop, departures[], alerts[], attribution | Prochains départs des transports en commun à un arrêt. |
vehicles_layer | objet contenant operator, vehicles[], shapes[], attribution | Positions des véhicules de transport en commun pour l’arrêt ou la ligne de la fiche. |
streetview_card | objet contenant lat, lng, image_id, image_lat, image_lng | Images au niveau de la rue près d’un lieu. |
Lorsqu’ils sont envoyés, end reprend cinq de ces éléments sous le même nom, afin qu’un client ayant manqué l’événement puisse toujours les lire : weather_card, satellite_card, constellation_layer, weather_layer et vehicles_layer (par exemple end.weather_card). Les autres fiches ne sont pas reprises dans end. Les listes d’événements en direct ne sont pas disponibles via l’API de la plateforme : voir Licences des données.
Diagnostic et expérimentation (hors contrat)
Ces éléments peuvent apparaître dans le flux. Ils ne font pas partie du contrat : ne vous appuyez pas dessus.
event: | Charge utile | Remarques |
|---|---|---|
debug | { scope, … } | Diagnostic interne. Absent des requêtes normales. |
place_name_found | { name, is_early } | Indice précoce indiquant qu’un nom de lieu a été mentionné. Utilisez l’événement place qui suit. |
Ordre des événements
Un flux passe par les étapes suivantes. Les étapes marquées facultatives ne figurent pas dans tous les flux.
- Facultatif. Événements
placeavecsource: "vendor": jusqu’à trois lieux provenant des données de votre organisation lorsque la clé dispose de la portéevendor. Ils sont envoyés en premier pour permettre à la carte de les afficher immédiatement. phase. Toujours le premier événement de la réponse elle-même.- Facultatif. Commentaires
: keep-alivependant la préparation de la réponse. - Facultatif. Événements précédant le texte :
prompt_options, ouroute_clearsuivi deroute, ainsi qu’une fiche. start.- Trames de texte entrecoupées de
place,place_linked,early_actionset parfois deroute_clear,routeet d’événements de couches. Les signaux de maintien de connexion couvrent les périodes de silence. - Facultatif. Événements tardifs
place,place_linked, de couches et d’itinéraires une fois le texte terminé. endouerror.- Facultatif.
quota. - Le serveur ferme la connexion.
Deux conséquences pour votre client :
endn’est pas le dernier événement. Continuez la lecture jusqu’à la fermeture du corps de réponse, sinon vous manquerezquota.- Un flux peut se fermer sans
endnierror, notamment en cas de rupture réseau. Considérez comme ayant échoué tout flux fermé sans événement terminal.
Détails des charges utiles
Texte et marqueurs
Les trames de texte ne contiennent que du texte ; les données structurées ne se trouvent jamais dans le texte. Celui-ci peut contenir des marqueurs à doubles crochets autour des noms de lieux, par exemple near [[Louvre Museum]], que le panneau de discussion transforme en liens vers les repères. Pour un affichage simple, retirez les crochets et conservez le nom.
place
| Champ | Type | Remarques |
|---|---|---|
| name | string | Clé utilisée pour fusionner les données. |
| coordinates | object { lat, lng } | Degrés décimaux. Peut être absent. Un lieu nommé mais non localisé est envoyé sans coordonnées avec unverified: true. Ne placez aucun repère. |
| address | string | Facultatif. |
| category | string | Facultatif. |
| icon | string | Facultatif. Un émoji représentant le lieu. |
| source | string | Facultatif. "vendor" indique un lieu tiré des données de votre organisation. "map" désigne un lieu déjà représenté sur la carte publiée : le panneau ne crée pas de repère supplémentaire, puisque la carte le fait déjà. |
| id | string | Facultatif. Présent pour les lieux issus de vos données. |
| unverified | boolean | Facultatif. true si l’emplacement du lieu n’a pas été confirmé. |
Un lieu peut comporter d’autres champs. Ignorez ceux que vous n’utilisez pas.
Le même nom peut être envoyé plusieurs fois : d’abord avec des coordonnées, puis avec davantage de détails. Conservez une seule entrée par nom, ne déplacez jamais un repère dès lors qu’il possède des coordonnées et laissez les év énements suivants compléter les autres champs.
Actions
early_actions.actions et end.actions sont des listes de { type, payload }.
| Type d’action | Charge utile | Action à effectuer |
|---|---|---|
| map.fitBounds | { coordinates: [[lng, lat], …], padding, maxZoom, duration } | Cadrez la caméra sur les coordonnées. |
| places.highlight | { places: [ { name, lat, lng } ], options } | Mettez ces lieux en évidence. |
| map.showRoute | { id, label, profile, coordinates: [[lng, lat], …], distance_m, duration_s, turn_by_turn[], transit?, attribution? } | Tracez la ligne et affichez son attribution. |
| route.clear | { id? } | Supprimez l’itinéraire avec cet id, ou tous les itinéraires si aucun id n’est donné. |
D’autres types d’actions peuvent apparaître. Ignorez ceux que vous ne gérez pas. Notez les deux formats de coordonnées : un lieu possède coordinates: { lat, lng }, tandis qu’une ligne est une liste de paires [lng, lat].
route
primary contient la ligne à tracer : { coordinates: [[lng, lat], …], distance_m, duration_s, turn_by_turn[] }. Chaque étape de turn_by_turn possède text et peut aussi contenir direction_type, distance_m, duration_s, lat et lng. origin et destination possèdent chacun un label. alternatives[] contient d’autres lignes avec un route_id et comparison[] indique le delta_duration_s de chaque alternative par rapport à l’itinéraire principal. Lorsqu’il est présent, stops[] contient des points ordonnés { lat, lng, name } d’un itinéraire à plusieurs arrêts.
Un itinéraire routier comporte attribution: { text, url, license }. Affichez text sous forme de lien vers url partout où vous tracez l’itinéraire. Pour les transports en commun, la mention de l’opérateur se trouve plutôt dans transit.
Chaque événement route possède un id. Tracer un itinéraire avec un id déjà existant remplace cet itinéraire.
end
| Champ | Type | Remarques |
|---|---|---|
| full_text | string | Réponse intégrale. Préférez-la aux trames de texte concaténées. |
| places | array | Liste définitive et ordonnée des lieux. Fusionnez-la par nom avec les lieux collectés. |
| actions | array | Actions finales sur la carte. Appliquez-les : elles remplacent early_actions. |
| dropped_places | array of strings | Facultatif. Noms envoyés plus tôt mais exclus de la réponse finale. Supprimez leurs repères. |
| question_type | string | Étiquette indiquant le type de question, à titre informatif. |
| response_time | number | Durée de la réponse en secondes. |
| total_tokens | number | Facultatif. Nombre de jetons utilisés, servant au calcul de la facturation. |
error
event: error
data: {"type": "error", "code": "upstream_stall", "message": "The AI service stopped responding. Please try again.", "partial_text": "The Louvre sits"}
message peut être présenté à l’utilisateur. partial_text correspond au texte généré avant l’échec. code n’apparaît que dans un seul cas :
| Cas | code | Action à effectuer |
|---|---|---|
| La réponse a cessé de progresser ou le flux a atteint sa taille maximale | "upstream_stall" | Proposez une nouvelle tentative. |
| Tout autre échec pendant la réponse | absent | Affichez message ; une nouvelle tentative peut réussir. |
Après error, aucun événement end n’est envoyé. Un événement quota peut encore suivre.
quota
Les champs dépendent du type d’identifiant ayant ouvert le flux.
| Champ | Type | Clé de serveur ou ancienne clé | Jeton de session |
|---|---|---|---|
| type | chaîne, "quota" | Oui | Oui |
| meter | chaîne | Oui | Oui |
| reset_at | chaîne, ISO 8601 avec décalage | Oui | Oui |
| product | chaîne | Oui | Non |
| period | chaîne | Oui | Non |
| tier | chaîne | Oui | Non |
| used | nombre | Oui | Non |
| limit | nombre | Oui | Non |
Le jeton de session est utilisé par les visiteurs d’une page : il est obtenu à partir d’une clé publiable ou d’une session de visiteur pour une carte publiée. Son événement quota indique la date de réinitialisation de la période sans révéler le forfait, l’utilisation ni la limite de l’organisation. Un client navigateur ne doit pas s’attendre à recevoir used ou limit. La même règle s’applique au corps de la réponse 429 quota_exceeded : voir Quotas et limites de débit.
Maintien de connexion et délais d’expiration
| Comportement | Valeur | Action du client |
|---|---|---|
| Signal de maintien du flux de discussion | Un commentaire : keep-alive après 10 secondes sans autre sortie | Réinitialisez le compteur d’inactivité dès la réception d’octets, y compris les commentaires. |
| Connexion inactive | Une connexion silencieuse pendant environ une minute peut être fermée pendant le transport | Fixez un délai d’inactivité (lecture) de 30 à 60 secondes. Évitez un délai global trop court. |
| Délai avant le premier texte | Peut prendre plusieurs dizaines de secondes pour une question difficile | Affichez la progression dès phase ; ne considérez pas l’attente comme un échec. |
| Durée maximale du flux | Environ 10 minutes, puis le serveur envoie error avec upstream_stall | Fixez un délai global légèrement supérieur à 10 minutes en dernier recours. |
Interruption d’un flux et facturation
Fermez la connexion pour interrompre le flux. Le serveur le détecte en une dizaine de secondes et arrête la réponse.
- Un flux qui se termine normalement est facturé selon le nombre de jetons utilisés, indiqué dans
end.total_tokens. - Un flux interrompu ou en échec avant la fin est facturé sur une estimation, au moins égale à la taille de la conversation envoyée dans la requête, ou calculée d’après le texte déjà transmis. De ce fait, le montant peut dépasser la réservation effectuée à l’admission : fermer la connexion dès la première trame ne rend pas gratuite une longue conversation.
- Les lieux issus de vos propres données (
source: "vendor") n’entrent pas dans le calcul.
Une nouvelle tentative constitue une nouvelle requête, facturée à nouveau. Voir Quotas et limites de débit.
Flux de résumés et de boutons
Ces deux points de terminaison alimentent la fiche d’un lieu dans Kaleidr Chat. Ils utilisent le même format de trame, les mêmes trames de texte sans nom et le même événement quota final. Ils diffèrent du flux de discussion sur trois points : aucun événement phase, aucun événement de lieu ou d’action, et aucun signal de maintien de connexion. Utilisez un délai d’inactivité de 60 secondes.
Les deux points de terminaison refusent un champ trop long avant le début du flux avec le statut 422 et le code field_too_long, et ignorent les champs non répertoriés : voir Points de terminaison. Si l’un de ces flux est interrompu ou échoue, sa facturation repose sur une estimation du texte déjà transmis et ne dépasse jamais le montant réservé.
POST /chat/summary/stream, pour un lieu :
- Facultatif.
addressavec la charge utile{ type: "address", address, source } startavec la charge utile{ type: "start", poi_type }- Facultatif, parfois répété.
urlavec la charge utile{ type: "url", website, source } - Trames de texte avec la charge utile
{ text } endavec la charge utile{ type: "end", full_text, summary, poi_type, category, city, country, sources[], website?, total_tokens? }, ouerroravec la charge utile{ type: "error", message, partial_text }- Facultatif.
quota
POST /chat/summary/stream, pour un bâtiment (type: "building") :
startavec la charge utile{ type: "start", address }- Trames de texte
endavec la charge utile{ type: "end", full_text, parsed, total_tokens? }, ouerror- Facultatif.
quota
POST /chat/button/stream:
startavec la charge utile{ type: "start", query_type }- Trames de texte
endavec la charge utile{ type: "end", full_text, reply, query_type, first_token_ms, total_ms, total_tokens? }- Facultatif.
quota
Le flux des boutons ne possède pas d’événement error. S’il échoue après avoir commencé, la connexion se ferme sans end. Un échec survenant avant son démarrage renvoie un statut HTTP 500 avec {"detail": "Button chat stream error"}.
POST /spot/overview/stream envoie les trames du résumé de lieu décrit ci-dessus, précédées d’une trame place contenant { type: "place", name } : le nom complet du lieu ou de la région décrits, par exemple "Springfield, Ohio, United States of America", pour l’étiqueter ou le copier. Elle arrive avant toute autre trame et peut être absente. Ce n’est pas l’événement place du flux de chat et elle ne porte aucune coordonnée. Si un ref ne peut être décrit, une seule trame error contenant { type: "error", message, ref } remplace le résumé. Voir Points de terminaison.
Voir aussi
- Lecture du flux : clients complets en Node et Python.
- Points de terminaison : corps de requête de chaque flux.
- Erreurs : échecs avant et pendant un flux.
- Quotas et limites de débit : événement
quotaet facturation.