Ce guide explique comment diffuser des messages de manière incrémentielle dans Google Chat à l'aide de la ressource StreamMessage dans l'API Chat. Le streaming permet à votre application Google Chat d'afficher des indicateurs de progression en direct, des étapes de raisonnement intermédiaire ou d'exécution d'outils, ainsi que des réponses textuelles incrémentielles avant de finaliser un message.
Le streaming est conçu pour les applications Chat optimisées par l'IA générative ou les workflows de backend de longue durée, où la génération d'une réponse complète prend plusieurs secondes ou minutes.
Fonctionnement du flux de messages
Un message en streaming passe par un état de streaming actif avant de devenir une ressource Message standard dans un espace :
- Démarrer un fil de discussion (
spaces.streamMessages.create) : votre application Chat crée unStreamMessagedans un espace organisé en fils de discussion, en référençant le message d'interaction utilisateur auquel elle répond. L'API affiche un indicateur de progression en direct dans le fil de discussion et renvoie un nom de ressourceStreamMessageet unnextChunkTokeninitial. - Mettre à jour le flux (
appendStreamMessageContentoupatch) : à mesure que votre application Chat génère une sortie, elle envoie des mises à jour séquentielles à l'aide du derniernextChunkToken:- Appelez
spaces.streamMessages.appendStreamMessageContentpour concaténer de nouveaux fragments de texte, ajouter des étapes intermédiaires, ajouter des citations intégrées ou mettre à jourprogressText. - Appelez
spaces.streamMessages.patchpour remplacer les fragments de contenu diffusés en continu ou mettre à jourprogressTextsur place.
- Appelez
- Finalisez ou supprimez le flux (
commitoudelete) :- Appelez
spaces.streamMessages.commitpour finaliser le flux et le convertir en ressourceMessagepermanente. Vous pouvez également joindre des éléments de commit uniquement, tels que des fiches, des widgets d'accessoires et des sources en pied de page, lors du commit. - Appelez
spaces.streamMessages.deletepour supprimer un flux actif sans enregistrer de message dans l'espace.
- Appelez
Lorsqu'un flux est actif, seul l'expéditeur du message référencé (referencedMessage) diffusé peut cliquer sur Arrêter pour annuler la génération. Chat envoie un événement de commande système d'annulation à votre application Chat afin que vous puissiez arrêter le travail du backend et valider une sortie partielle ou supprimer le flux.
Prérequis
Pour diffuser des messages en streaming avec l'API Chat, remplissez les conditions préalables suivantes :
- Compte Google Workspace Business ou Enterprise ayant accès à Google Chat.
- Configurez votre environnement :
- Créez un projet Google Cloud.
- Activez et configurez l'API Google Chat avec un nom, une icône et une description pour votre application Chat.
- Créez des identifiants de compte de service et enregistrez-les sous le nom
credentials.json. Le streaming de messages nécessite l'authentification de l'application avec le champ d'application d'autorisationhttps://www.googleapis.com/auth/chat.bot. L'authentification des utilisateurs n'est pas acceptée.
- Ajoutez votre application Chat à un espace Chat avec fils de discussion (ou à un message privé qui contient déjà au moins un message), puis obtenez le nom de ressource d'un message accessible auquel répondre (
spaces/SPACE_ID/messages/MESSAGE_ID). Pour les exemples Python, installez la bibliothèque cliente des API Google pour Python et
google-auth, puis initialisez le client de service de l'API Chat :pip install google-api-python-client google-authfrom google.oauth2 import service_account from googleapiclient.discovery import build SCOPES = ["https://www.googleapis.com/auth/chat.bot"] creds = service_account.Credentials.from_service_account_file( "credentials.json", scopes=SCOPES ) chat = build("chat", "v1", credentials=creds)
Lancer un flux
Pour commencer à diffuser une réponse, appelez spaces.streamMessages.create et transmettez un StreamMessage dans le corps de la requête :
referencedMessage(obligatoire) : nom de ressource du message qui a déclenché la réponse, au formatspaces/{space}/messages/{message}. Le message référencé doit appartenir au même espace et être accessible à votre application Chat. Chat publie la réponse diffusée dans le même fil de discussion quereferencedMessage.progressText(obligatoire) : libellé d'état en texte brut non vide (jusqu'à 250 caractères) affiché à côté de l'indicateur de progression, par exemple"Thinking...".markupSyntax(facultatif) : syntaxe de mise en forme du texte pour le message, par exempleMARKUP_SYNTAX_MARKDOWNouMARKUP_SYNTAX_CHAT(par défaut, la syntaxe configurée de l'application Chat siMARKUP_SYNTAX_UNSPECIFIED). DéfinissezMARKUP_SYNTAX_MARKDOWNsi vous prévoyez d'inclure des citations intégrées.streamContentChunks(facultatif) : fragmentstext,stepoucitationinitiaux à afficher immédiatement.
Enregistrez name (spaces/{space}/streamMessages/{stream_message}) et nextChunkToken à partir de la réponse pour les appels ultérieurs.
HTTP
POST https://chat.googleapis.com/v1/spaces/SPACE_ID/streamMessages HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
{
"referencedMessage": "spaces/SPACE_ID/messages/MESSAGE_ID",
"progressText": "Thinking...",
"markupSyntax": "MARKUP_SYNTAX_MARKDOWN"
}
Python
stream = (
chat.spaces()
.streamMessages()
.create(
parent="spaces/SPACE_ID",
body={
"referencedMessage": "spaces/SPACE_ID/messages/MESSAGE_ID",
"progressText": "Thinking...",
"markupSyntax": "MARKUP_SYNTAX_MARKDOWN",
},
)
.execute()
)
stream_name = stream["name"]
chunk_token = stream["nextChunkToken"]
La méthode renvoie une ressource StreamMessage :
{
"name": "spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID",
"progressText": "Thinking...",
"markupSyntax": "MARKUP_SYNTAX_MARKDOWN",
"nextChunkToken": "INITIAL_CHUNK_TOKEN"
}
Ajouter des fragments de contenu
Lorsque votre application Chat produit des étapes intermédiaires ou des jetons de texte, appelez spaces.streamMessages.appendStreamMessageContent :
- Transmettez la dernière version de
nextChunkTokendanschunkToken. Chaque appel réussi renvoie un nouveaunextChunkTokenque vous devez transmettre lors de votre prochaine requête de mise à jour ou de commit. - Les fragments
textsont concaténés directement sur le texte précédemment diffusé. Incluez des espaces ou des sauts de ligne si nécessaire entre les fragments. - Les fragments
stepsont ajoutés à la liste des étapes intermédiaires affichées dans le message. progressTextest facultatif. Si vous omettezprogressTextou si vous le définissez sur"", Chat continue d'afficher leprogressTextprécédemment défini.
HTTP
POST https://chat.googleapis.com/v1/spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID:appendStreamMessageContent HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
{
"chunkToken": "INITIAL_CHUNK_TOKEN",
"progressText": "Searching documentation...",
"streamContentChunks": [
{
"step": {
"title": "Search knowledge base",
"description": "Found 3 matching articles."
}
},
{
"text": "See <chat-citation data-id=\"c1\">guide</chat-citation>:\n"
},
{
"citation": {
"id": "c1",
"citedSources": [
{
"title": "System Architecture Guide",
"uri": "https://example.com/docs/arch"
}
]
}
}
]
}
Python
stream = (
chat.spaces()
.streamMessages()
.appendStreamMessageContent(
name=stream_name,
body={
"chunkToken": chunk_token,
"progressText": "Searching documentation...",
"streamContentChunks": [
{
"step": {
"title": "Search knowledge base",
"description": "Found 3 matching articles.",
}
},
{
"text": (
'See <chat-citation data-id="c1">'
"guide</chat-citation>:\n"
)
},
{
"citation": {
"id": "c1",
"citedSources": [
{
"title": "System Architecture Guide",
"uri": "https://example.com/docs/arch",
}
],
}
},
],
},
)
.execute()
)
chunk_token = stream["nextChunkToken"]
Remplacer le contenu diffusé en streaming ou le texte de progression
Si vous devez réécrire du contenu précédemment diffusé (par exemple, après avoir exécuté un filtre de post-traitement ou de sécurité) ou ne mettre à jour que progressText sans ajouter de fragments, appelez spaces.streamMessages.patch :
- Définissez
updateMasksur une liste de champs à mettre à jour, séparés par une virgule :progressText,streamContentChunksouprogressText,streamContentChunks. - Incluez le dernier
nextChunkTokendans le corps de la requêteStreamMessageet enregistrez le nouveaunextChunkTokenrenvoyé dans la réponse. - Lorsque
streamContentChunksest défini surupdateMask, les fragments fournis remplacent le contenu précédemment diffusé au lieu de s'y ajouter. Lorsque
progressTextest dansupdateMask,progressTextdoit être un texte brut non vide de 250 caractères maximum.
HTTP
PATCH https://chat.googleapis.com/v1/spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID?updateMask=progressText,streamContentChunks HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
{
"nextChunkToken": "LATEST_CHUNK_TOKEN",
"progressText": "Formatting response...",
"streamContentChunks": [
{
"text": "Updated response text."
}
]
}
Python
stream = (
chat.spaces()
.streamMessages()
.patch(
name=stream_name,
updateMask="progressText,streamContentChunks",
body={
"nextChunkToken": chunk_token,
"progressText": "Formatting response...",
"streamContentChunks": [
{"text": "Updated response text."}
],
},
)
.execute()
)
chunk_token = stream["nextChunkToken"]
Finaliser un flux
Une fois la génération terminée, appelez spaces.streamMessages.commit avec le dernier chunkToken pour finaliser le flux dans une ressource Message permanente :
- Vous pouvez éventuellement inclure des
streamContentChunksfinaux dans la requête de commit, y compris des types de fragments réservés au commit (card,accessoryWidgetetcitedSource). Si vous avez déjà diffusé du contenu non vide lors d'appels précédents, vous pouvez omettre
streamContentChunkslors de la validation. Le message de commit final ne peut pas être vide.
HTTP
POST https://chat.googleapis.com/v1/spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID:commit HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
{
"chunkToken": "LATEST_CHUNK_TOKEN",
"streamContentChunks": [
{
"text": "All checks passed."
},
{
"citedSource": {
"title": "Deployment Runbook",
"uri": "https://example.com/docs/runbook"
}
},
{
"card": {
"cardId": "details-card",
"card": {
"header": {
"title": "Build Status"
},
"sections": [
{
"widgets": [
{
"textParagraph": {
"text": "All 42 checks succeeded."
}
}
]
}
]
}
}
}
]
}
Python
status_card = {
"cardId": "details-card",
"card": {
"header": {"title": "Build Status"},
"sections": [
{
"widgets": [
{"textParagraph": {"text": "All 42 checks succeeded."}}
]
}
],
},
}
commit_response = (
chat.spaces()
.streamMessages()
.commit(
name=stream_name,
body={
"chunkToken": chunk_token,
"streamContentChunks": [
{"text": "All checks passed."},
{
"citedSource": {
"title": "Deployment Runbook",
"uri": "https://example.com/docs/runbook",
}
},
{"card": status_card},
],
},
)
.execute()
)
final_message = commit_response["message"]
print(f"Committed message: {final_message['name']}")
Supprimer un flux actif
Si une erreur irrécupérable se produit ou si un utilisateur annule la génération et que vous ne souhaitez pas laisser de réponse partielle dans le fil de discussion, appelez spaces.streamMessages.delete :
HTTP
DELETE https://chat.googleapis.com/v1/spaces/SPACE_ID/streamMessages/STREAM_MESSAGE_ID HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Python
chat.spaces().streamMessages().delete(name=stream_name).execute()
Une fois qu'un flux est validé dans un Message, appelez plutôt spaces.messages.delete si vous devez supprimer le message finalisé.
Types de fragments de contenu compatibles
Chaque entrée de streamContentChunks spécifie un champ de l'union StreamContentChunk. Certains types de fragments peuvent être envoyés tout au long du flux, tandis que d'autres ne sont acceptés que dans spaces.streamMessages.commit :
| Champ de fragment | Méthodes acceptées | Champ Message final |
Remarques |
|---|---|---|---|
text |
create, appendStreamMessageContent,
patch, commit |
text |
Texte incrémental. Concaténation le appendStreamMessageContent ; remplacement le patch.
Le texte final validé ne peut pas dépasser 32 000 octets. |
step |
create, appendStreamMessageContent,
patch, commit |
elements.steps.steps[] |
Étape de traitement intermédiaire avec title en texte brut (obligatoire) et description (facultatif). |
citation |
create, appendStreamMessageContent,
patch, commit |
elements.citations[] |
Remplacez Citation par un id alphanumérique (moins de 63 caractères, traits de soulignement autorisés) et citedSources. Nécessite des balises markupSyntax: "MARKUP_SYNTAX_MARKDOWN" et <chat-citation data-id="ID">...</chat-citation> correspondantes dans text. L'envoi d'un citation avec un id existant écrase la citation précédente. |
card |
commit uniquement |
cardsV2[] |
Carte interactive ou d'information CardWithId. |
accessoryWidget |
commit uniquement |
accessoryWidgets[] |
AccessoryWidget : boutons affichés sous le message, tels que les boutons de commentaires. |
citedSource |
commit uniquement |
elements.citedSources[] |
CitedSource : lien de pied de page affiché sous le message sans balises de citation intégrées. |
Gérer l'annulation du flux initiée par l'utilisateur
Lorsqu'un flux est actif, seul l'expéditeur du message référencé (referencedMessage) sur lequel le flux est diffusé peut cliquer sur Arrêter sur le message de diffusion. Lorsque l'utilisateur arrête un flux, Chat envoie un événement d'interaction de commande système contenant cancelStream.streamMessage (le nom de ressource spaces/{space}/streamMessages/{stream_message} du flux arrêté) au point de terminaison de votre application Chat.
La structure de la charge utile JSON entrante dépend de la façon dont vous créez votre application Chat : en tant que module complémentaire Google Workspace ou à l'aide d'événements d'interaction de l'API Chat.
Modules complémentaires Google Workspace
{
"commonEventObject": {
"hostApp": "CHAT"
},
"chat": {
"user": {
"name": "users/USER_ID"
},
"space": {
"name": "spaces/SPACE_ID"
},
"systemCommandPayload": {
"systemCommandMetadata": {
"cancelStream": {
"streamMessage": "spaces/SPACE_ID/streamMessages/STREAM_ID"
}
}
}
}
}
API Google Chat
{
"type": "SYSTEM_COMMAND",
"space": {
"name": "spaces/SPACE_ID"
},
"user": {
"name": "users/USER_ID"
},
"systemCommandMetadata": {
"cancelStream": {
"streamMessage": "spaces/SPACE_ID/streamMessages/STREAM_ID"
}
}
}
Lorsque votre application Chat reçoit un événement cancelStream :
- Signalez à votre nœud de calcul en arrière-plan d'arrêter de générer d'autres jetons ou d'exécuter des outils en aval pour
streamMessage. - Résolvez le flux actif en utilisant l'une des approches suivantes :
- Conserver la sortie partielle : appelez
spaces.streamMessages.commitavec le dernierchunkToken(en ajoutant éventuellement une note telle que"\n\n_Generation stopped._"). - Supprimer la réponse : appelez
spaces.streamMessages.deletepour que rien ne soit enregistré dans le fil de discussion.
- Conserver la sortie partielle : appelez
- Renvoyez une réponse JSON HTTP
200 OKvide ({}) à partir de votre gestionnaire d'événements d'interaction.
Limites
Le streaming de messages présente les limites suivantes :
- Disponibilité de la plate-forme : le flux de messages n'est visible que dans Chat sur le Web. La compatibilité avec Android et iOS sera bientôt disponible.
- Délai d'expiration du flux : un
StreamMessageactif expire 10 minutes après sa création. Votre application Chat doit appelerspaces.streamMessages.commitouspaces.streamMessages.deletedans les 10 minutes suivant le début du flux.