Diffusion en continu
La diffusion en continu est pilotée par les capacités. Les fournisseurs qui implémentent les méthodes de diffusion et retournent supports_streaming() == true peuvent émettre des deltas de jetons ; les autres fournisseurs utilisent le chemin de réponse sans diffusion. Le moteur d’exécution transmet les flux disponibles aux adaptateurs de canal qui prennent en charge les mises à jour partielles.
Qu’est-ce qui est diffusé
Le trait provider émet des valeurs StreamEvent au fur et à mesure que le modèle génère sa sortie : deltas de texte, appels d’outils structurés, appels d’outils pré-exécutés côté provider et leurs résultats, rapports d’utilisation de tokens, et un marqueur final de complétion. Les définitions par variante faisant autorité se trouvent avec le type dans crates/zeroclaw-api/src/model_provider.rs (enum StreamEvent) ; les tokens de raisonnement arrivent sous forme de deltas de texte, et non comme une variante distincte.
Le runtime consomme ces événements. L’orchestrateur de canaux utilise les méthodes de livraison de brouillon du trait Channel et les indicateurs de capacité pour afficher une sortie progressive lorsque cela est pris en charge.
Indicateurs de capacité
Un fournisseur expose deux indicateurs afin que l’exécution sache ce qu’elle peut attendre :
#![allow(unused)]
fn main() {
fn supports_streaming(&self) -> bool { false }
fn supports_streaming_tool_events(&self) -> bool { false }
}
supports_streaming: true uniquement lorsque le fournisseur concret opte pour le streaming ; la valeur par défaut du trait est falsesupports_streaming_tool_events: true lorsque le fournisseur émet des événementsToolCallpendant le flux plutôt qu’à la fin
Les fournisseurs compatibles avec OpenAI diffèrent : certains diffusent les deltas des arguments d’appel d’outil par morceaux, tandis que d’autres n’émettent l’appel qu’une fois terminé. L’analyseur SSE de compatible.rs gère les deux cas.
Diffusion côté canal
Les channels annoncent leurs propres capacités de streaming via le trait Channel :
#![allow(unused)]
fn main() {
fn supports_draft_updates(&self) -> bool; // modifier un message en place
fn supports_multi_message_streaming(&self) -> bool; // diviser une réponse en plusieurs messages
}
La capacité d’un canal découle de sa configuration : un canal avec l’énumération stream_mode (off / partial / multi_message) prend en charge à la fois les mises à jour de brouillon et le multi-message ; un canal avec le booléen stream_drafts prend en charge uniquement les mises à jour de brouillon. Ce tableau est généré à partir du schéma de configuration des canaux, il reste donc correct à mesure que les canaux gagnent ou perdent la prise en charge du streaming :
| Canal | Mises à jour de la version de développement | Multi-message |
|---|---|---|
discord | ✓ | ✓ |
lark | ✓ | ✓ |
matrix | ✓ | ✓ |
nextcloud_talk | ✓ | ✓ |
slack | ✓ | |
telegram | ✓ | ✓ |
wecom_ws | ✓ | ✓ |
Lorsque le fournisseur et le canal prennent tous deux en charge le streaming, le flux est le suivant : le fournisseur émet TextDelta → le runtime le transmet au canal → le canal modifie le message envoyé. La cadence de modification est limitée par le paramètre draft_update_interval_ms de ce canal afin d’éviter la limitation de débit ; les valeurs par défaut varient selon le canal.
Blocs de raisonnement
StreamEvent n’a pas de variante ReasoningDelta distincte. Lorsqu’un fournisseur expose le raisonnement pendant le streaming, il utilise le champ reasoning sur le StreamChunk transmis par TextDelta ; la configuration du fournisseur et du runtime détermine si ce contenu est demandé ou exposé. Les consommateurs doivent suivre le contrat StreamChunk plutôt que de rechercher une variante d’événement inexistante.
Appels d’outils en cours de route
Lorsqu’un fournisseur de streaming décide d’appeler un outil, il émet un événement de flux ToolCall structuré. L’environnement d’exécution :
- Lit le flux jusqu’à son terme, en collectant les événements
ToolCallstructurés et en transmettant le texte visible jusqu’àFinal - Récupère les appels d’outils à la fin du flux
- Exécute les outils (sous réserve de validation de sécurité, voir Sécurité → Vue d’ensemble)
- Ouvre un nouvel appel en streaming auprès du fournisseur pour le prochain tour de l’assistant, avec les résultats des outils ajoutés à la conversation
Le flux du fournisseur actuel n’est jamais mis en pause puis repris au milieu de la lecture ; l’exécution de l’outil a lieu une fois que ce flux atteint Final, et le tour suivant est un nouvel appel de streaming.
Du point de vue de l’utilisateur : du texte, puis un indicateur visible que l’agent a exécuté un outil (via des indices spécifiques au canal), puis plus de texte. Pour les canaux sans indicateurs de frappe, l’intervalle entre l’appel d’outil et le prochain bloc de texte est le seul signal.
Achèvement du transport et délais d’expiration
Les transports en streaming ne s’appuient pas sur la fermeture de la connexion comme signal de réussite. Les flux compatibles avec OpenAI se terminent par [DONE], les flux OpenAI Responses se terminent lors de leur événement de réponse terminal, et les flux Anthropic se terminent par message_stop. Les serveurs peuvent maintenir la connexion HTTP ouverte après ces événements.
Les clients de streaming utilisent des délais d’inactivité des octets : 300 secondes pour OpenAI Responses et les fournisseurs compatibles avec OpenAI, et 90 secondes pour Anthropic. Chaque lecture du corps reçue réinitialise le délai d’expiration correspondant, de sorte que les générations actives ne sont pas limitées par le délai d’expiration de la requête complète utilisé pour les appels sans streaming. L’établissement de la connexion, les en-têtes de réponse et les corps d’erreur mis en mémoire tampon restent soumis à des limites.
Fournisseurs non diffusants
Lorsque supports_streaming() est false, les appelants utilisent le chemin de chat non-streaming du fournisseur. Les adaptateurs de canal peuvent toujours envoyer la réponse complète, mais ils ne reçoivent pas les événements de flux de fournisseur incrémentiels.
Références de code
crates/zeroclaw-api/src/model_provider.rs: traitModelProvider, énumérationStreamEventcrates/zeroclaw-providers/src/compatible.rs: analyseur SSE compatible OpenAIcrates/zeroclaw-providers/src/anthropic.rs: streaming Anthropiccrates/zeroclaw-providers/src/ollama.rs: streaming Ollamacrates/zeroclaw-channels/src/orchestrator/mod.rs: consommation du flux côté canal