Stream and Run
The two adapters an Agent uses to talk to a Haystack model: Stream (streaming chat) and Run (non-streaming calls).

| Adapter | Purpose | Powered by |
|---|---|---|
Stream |
Streaming chat | A haystack.Pipeline (usually a ToolAgent) |
Run |
Non-streaming calls | A generator with .run() |
An agent exposes one of each via get_pipeline_adapter() and get_run_adapter().
Run
Run wraps a generator for single-shot calls. It converts internal ChatMessages to Haystack messages, runs the generator, and returns one reply.
Run(
generator: Any, # Haystack chat generator with .run()
model: str | None = None,
instructions: str | None = None,
)response = await run.run(
messages: list[ChatMessage],
system_prompt: str | None = None,
response_format: type[T] | None = None,
) # -> T | str | None- Without
response_format, returns the reply text as astr. - With
response_format, passes the Pydantic model’s JSON schema as the generation’sresponse_formatand returns a validated instance.
from pydantic import BaseModel
class Movie(BaseModel):
title: str
year: int
class MyAgent(Agent):
async def get_run_adapter(self, thread_id=None, user=None):
generator = OpenAIChatGenerator(...)
return Run(generator=generator)
# agent.run(messages, response_format=Movie) -> Movie
# agent.run(messages) -> strRun.get_messages() keeps only user and assistant messages and converts them to Haystack ChatMessages; a system_prompt passed to run() is prepended as a system message.
Stream
Stream wraps a Haystack pipeline and normalizes its streaming output into StreamEvents.
Stream(
pipeline: Pipeline, # haystack.Pipeline instance
generator: Any, # generator used for streaming
store: bool = True, # persist the reply
storage_adapter: BaseStorageAdapter | None = None,
citation_registry: CitationRegistry | None = None,
suggestion_generator: SuggestionGenerator | None = None,
)pipeline must be a real haystack.Pipeline: anything else raises TypeError. A ToolAgent exposes its pipeline via tool_agent.pipeline().Pipeline vs Agent Component
If the pipeline’s first component is a Haystack Agent, Stream calls agent.run_async(...) directly with the streaming callback. Otherwise it runs pipeline.run_async({"messages": ...}). Both paths stream chunks through a queue that Stream.get_events() consumes as events.
stream()
stream() is an async generator:
async for event in stream.stream(messages: list[ChatMessage]):
... # handle StreamEventOrder of operations:
- Convert
ChatMessages to Haystack messages (merge_messagescontrols consecutive-message merging). - Generate the
message_idUUID exactly once. - Create a
StreamWriter(whenstoreand astorage_adapterare set). - Attach the streaming callback to the generator, warm it up if it has
warm_up. - Yield
MessageStartEvent, live events,MessageEndEvent, optionalSuggestionEvent, thenStreamEndEvent.
Examples
Stream a ToolAgent
stream = Stream(
pipeline=tool_agent.pipeline(),
generator=generator,
storage_adapter=storage,
citation_registry=await agent.get_citation_registry(),
suggestion_generator=agent.suggestion_generator,
)Stream a Bare Pipeline
pipeline = Pipeline()
pipeline.add_component("prompt", ChatPromptBuilder(template=[...]))
pipeline.add_component("llm", generator)
pipeline.connect("prompt.prompt", "llm.messages")
stream = Stream(pipeline=pipeline, generator=generator)
async for event in stream.stream(messages):
...Next: Stream Events, what stream() yields.