The harness must support streaming independently from the graph. Graph streaming can forward harness events, but direct harness users should also be able to consume streams.
- Stream model token/message deltas.
- Stream tool progress.
- Stream usage and cost updates.
- Stream cache hit/miss events.
- Stream summary events.
- Stream final outputs.
- Forward events into the registry event bus.
- Merge provider chunks into a final assistant message.
- Preserve streamed tool-call chunks where providers support them.
- Support cancellation and backpressure.
- Support stream replay from event stores.
LangChain exposes model streams, runnable event streams, callback streaming, and tracer streams:
- chat model stream: https://github.com/langchain-ai/langchain/blob/master/libs/core/langchain_core/language_models/chat_model_stream.py
- runnable event streams: https://github.com/langchain-ai/langchain/tree/master/libs/core/langchain_core/runnables
- streaming tracers: https://github.com/langchain-ai/langchain/tree/master/libs/core/langchain_core/tracers
- provider streaming adapters such as OpenAI: https://github.com/langchain-ai/langchain/blob/master/libs/partners/openai/langchain_openai/chat_models/base.py
messages: model deltas and final messagestools: tool lifecycle and progressusage: token updatescost: price updatesevents: all harness eventsfinal: final result only
Every stream item should carry run ids and component ids so web UIs can merge harness streams with graph streams.
pub enum HarnessStreamItem {
Event(HarnessEvent),
MessageDelta(MessageDelta),
ToolCallDelta(ToolCallDelta),
ToolProgress(ToolProgress),
Usage(UsageRecord),
Cost(CostRecord),
Final(AgentRun),
}A stream consumer should be able to subscribe to a subset of modes without changing execution. Dropping a consumer must not cancel the run unless the consumer owns the run cancellation token.
Streaming adapters must merge chunks deterministically:
- text chunks preserve order
- reasoning/thinking chunks preserve order on a side channel
- content block indexes are respected
- tool-call chunks are correlated by id or index
- cumulative usage is converted into deltas or clearly marked cumulative
- final message equals the merged stream
- invalid partial tool calls are surfaced as repairable parse errors