Transmisión
El streaming está basado en capacidades. Los proveedores que implementan los métodos de streaming y devuelven supports_streaming() == true pueden emitir deltas de tokens; otros proveedores utilizan la ruta de respuesta sin streaming. El runtime reenvía los streams disponibles a los adaptadores de canal que admiten actualizaciones parciales.
¿Qué se transmite?
El trait de proveedor emite valores StreamEvent a medida que el modelo genera salida: deltas de texto, llamadas a herramientas estructuradas, llamadas a herramientas preejecutadas del lado del proveedor y sus resultados, informes de uso de tokens y un marcador final de finalización. Las definiciones autoritativas por variante se encuentran junto al tipo en crates/zeroclaw-api/src/model_provider.rs (enum StreamEvent); los tokens de razonamiento llegan como deltas de texto, no como una variante separada.
El runtime consume estos eventos. El orquestador de canales utiliza los métodos de entrega de borradores y los indicadores de capacidad del trait Channel para mostrar salida progresiva donde esté disponible.
Banderas de capacidad
Un proveedor expone dos indicadores para que el tiempo de ejecución sepa qué puede esperar:
#![allow(unused)]
fn main() {
fn supports_streaming(&self) -> bool { false }
fn supports_streaming_tool_events(&self) -> bool { false }
}
supports_streaming: verdadero solo cuando el proveedor concreto habilita el streaming; el valor predeterminado del trait es falsesupports_streaming_tool_events: true cuando el proveedor emite eventosToolCalldurante el stream en lugar de al final
Los proveedores compatibles con OpenAI difieren: algunos transmiten los deltas de los argumentos de la llamada a la herramienta por fragmentos, mientras que otros solo emiten la llamada una vez que está completa. El analizador SSE de compatible.rs maneja ambos casos.
Transmisión por el lado del canal
Los canales anuncian sus propias capacidades de streaming a través del trait Channel:
#![allow(unused)]
fn main() {
fn supports_draft_updates(&self) -> bool; // editar un mensaje en su lugar
fn supports_multi_message_streaming(&self) -> bool; // dividir una respuesta en varios mensajes
}
La capacidad de un canal se deriva de su configuración: un canal con el enum stream_mode (off / partial / multi_message) admite tanto actualizaciones de borradores como multi-mensaje; un canal con el booleano stream_drafts admite solo actualizaciones de borradores. Esta tabla se genera a partir del esquema de configuración del canal, por lo que se mantiene correcta a medida que los canales ganan o pierden soporte de streaming:
| Canal | Actualizaciones de borrador | Mensajes múltiples |
|---|---|---|
discord | ✓ | ✓ |
lark | ✓ | ✓ |
matrix | ✓ | ✓ |
nextcloud_talk | ✓ | ✓ |
slack | ✓ | |
telegram | ✓ | ✓ |
wecom_ws | ✓ | ✓ |
Cuando tanto el proveedor como el canal admiten streaming, el flujo es: el proveedor emite TextDelta → el runtime lo pasa al canal → el canal edita el mensaje enviado. La cadencia de edición está limitada por la configuración draft_update_interval_ms de ese canal para evitar la limitación de velocidad; los valores predeterminados varían según el canal.
Bloques de razonamiento
StreamEvent no tiene una variante ReasoningDelta separada. Cuando un proveedor expone razonamiento durante el streaming, utiliza el campo reasoning en el StreamChunk transportado por TextDelta; la configuración del proveedor y del runtime determina si ese contenido se solicita o se expone. Los consumidores deben seguir el contrato de StreamChunk en lugar de hacer coincidir una variante de evento inexistente.
Llamadas de herramientas en medio del flujo
Cuando un proveedor de streaming decide llamar a una herramienta, emite un evento de streaming estructurado ToolCall. El entorno de ejecución:
- Lee el flujo hasta completarlo, recopila eventos estructurados de
ToolCally reenvía el texto visible hastaFinal - Recupera las llamadas a herramientas después de que finaliza el flujo
- Ejecuta las herramientas (sujeto a la validación de seguridad; consulta Seguridad → Descripción general)
- Abre una nueva llamada de streaming al proveedor para el siguiente turno del asistente, con los resultados de las herramientas añadidos a la conversación
El flujo actual del proveedor nunca se pausa ni se reanuda a mitad de una lectura; la ejecución de herramientas tiene lugar después de que ese flujo alcanza Final, y el turno siguiente es una nueva llamada de streaming.
Desde la perspectiva del usuario: texto, luego un indicador visible de que el agente ejecutó una herramienta (mediante pistas específicas del canal), y después más texto. Para los canales sin indicadores de escritura, la brecha entre la llamada a la herramienta y el siguiente fragmento de texto es la única señal.
Finalización del transporte y tiempos de espera
Los transportes de streaming no dependen del cierre de la conexión como señal de éxito. Los flujos compatibles con OpenAI finalizan con [DONE], los flujos de OpenAI Responses finalizan con su evento de respuesta terminal y los flujos de Anthropic finalizan con message_stop. Los servidores pueden mantener abierta la conexión HTTP después de esos eventos.
Los clientes de streaming usan tiempos de espera por inactividad de bytes: 300 segundos para OpenAI Responses y los proveedores compatibles con OpenAI, y 90 segundos para Anthropic. Cada lectura del cuerpo recibida restablece el tiempo de espera correspondiente, por lo que las generaciones activas no están limitadas por el tiempo de espera de la solicitud completa que se usa para las llamadas sin streaming. El establecimiento de la conexión, los encabezados de respuesta y los cuerpos de error almacenados en búfer siguen estando limitados.
Proveedores no basados en streaming
Cuando supports_streaming() es false, los llamadores utilizan la ruta de chat no-streaming del proveedor. Los adaptadores de canal aún pueden enviar la respuesta completada, pero no reciben eventos de stream incrementales del proveedor.
Referencias de código
crates/zeroclaw-api/src/model_provider.rs: traitModelProvider, enumStreamEventcrates/zeroclaw-providers/src/compatible.rs: analizador SSE compatible con OpenAIcrates/zeroclaw-providers/src/anthropic.rs: streaming de Anthropiccrates/zeroclaw-providers/src/ollama.rs: Streaming de Ollamacrates/zeroclaw-channels/src/orchestrator/mod.rs: consumo de streams del lado del canal