Releases: deepset-ai/haystack
v2.27.0
⭐️ Highlights
🔌 Automatic List Joining in Pipeline
When a component expects a list as input, pipelines now automatically join multiple inputs into that list (no extra components needed), even if they come in different but compatible types. This enables patterns like combining a plain query string with a list of ChatMessage objects into a single list[ChatMessage] input.
Supported conversations:
| Source Types | Target Type | Behavior |
|---|---|---|
| T + T | list[T] | Combines multiple inputs into a list of the same type. |
| T + list[T] | list[T] | Merges single items and lists into a single list. |
| str + ChatMessage | list[str] | Converts all inputs to str and combines into a list. |
| str + ChatMessage | list[ChatMessage] | Converts all inputs to ChatMessage and combines into a list. |
Learn more about how to simplify list joins in pipelines in 📖 Smart Pipeline Connections: Implicit List Joining
🗄️ Better Developer Experience for DocumentStores
The metadata inspection and filtering utilities (count_documents_by_filter, count_unique_metadata_by_filter, get_metadata_field_min_max, etc.) are now available in the InMemoryDocumentStore, aligning it with other document stores.
You can prototype locally in memory and easily debug, filter, and inspect the data in the document store during development, then reuse the same logic in production. See all available methods in InMemoryDocumentStore API reference.
🚀 New Features
-
Added new operations to the
InMemoryDocumentStore: count_documents_by_filter, count_unique_metadata_by_filter, get_metadata_fields_info, get_metadata_field_min_max, get_metadata_field_unique_values -
AzureOpenAIChatGeneratornow exposes aSUPPORTED_MODELSclass variable listing supported model IDs, for examplegpt-5-miniandgpt-4o. To view all supported models go to the API reference or run:from haystack.components.generators.chat import AzureOpenAIChatGenerator print(AzureOpenAIChatGenerator.SUPPORTED_MODELS)
We will add this for other model providers in their respective ChatGenerator components step by step.
-
Added partial support for the
image-text-to-texttask inHuggingFaceLocalChatGenerator.This allows the use of multimodal models like Qwen 3.5 or Ministral with text-only inputs. Complete multimodal support via Hugging Face Transformers might be addressed in the future.
-
Added async filter helpers to the
InMemoryDocumentStore:update_by_filter_async(),count_documents_by_filter_async(), andcount_unique_metadata_by_filter_async().
⚡️ Enhancement Notes
- Add async variants of metadata methods to
InMemoryDocumentStore:get_metadata_fields_info_async(),get_metadata_field_min_max_async(), andget_metadata_field_unique_values_async(). These rely on the store's thread-pool executor, consistent with the existing async method pattern. - Add
_to_trace_dictmethod toImageContentandFileContentdataclasses. When tracing is enabled, the large base64-encoded binary fields (base64_imageandbase64_data) are replaced with placeholder strings (e.g."Base64 string (N characters)"), consistent with the behavior ofByteStream._to_trace_dict. - Pipelines now support auto-variadic connections with type conversion. When multiple senders are connected to a single list-typed input socket, the senders no longer need to all produce the exact same type since compatible conversions are applied per edge. Supported scenarios include
T + T -> list[T],T + list[T] -> list[T],str + ChatMessage -> list[str],str + ChatMessage -> list[ChatMessage], and all otherstr <-> ChatMessageconversion variants. This enables pipeline patterns like joining a plain query string with a list ofChatMessageobjects into a singlelist[ChatMessage]input without any extra components.
🔒 Security Notes
-
Fixed an issue in
ChatPromptBuilderwhere specially crafted template variables could be interpreted as structured content (e.g., images, tool calls) instead of plain text.Template variables are now automatically sanitized during rendering, ensuring they are always treated as plain text.
🐛 Bug Fixes
- Fix malformed log format string in
DocumentCleaner. The warning for documents withNonecontent used%{document_id}instead of{document_id}, preventing proper interpolation of the document ID. - Fix
ToolInvoker._merge_tool_outputssilently appendingNoneto list-typed state when a tool'soutputs_to_statesource key is absent from the tool result. This is a common scenario withPipelineToolwrapping a pipeline that has conditional branches where not all outputs are always produced even if defined inoutputs_to_state. The mapping is now skipped entirely when the source key is not present in the result dict. - Fixed an off-by-one error in
InMemoryDocumentStore.write_documentsthat caused the BM25 average document length to be systematically underestimated. - Resolve
$defs/$refin tool parameter schemas before sending them to the HuggingFace API. The HuggingFace API does not support JSON Schema$defsreferences, which are generated by Pydantic when tool parameters contain dataclass types. This fix inlines all$refpointers and removes the$defssection from tool schemas inHuggingFaceAPIChatGenerator. - The default
bm25_tokenization_regexinInMemoryDocumentStorenow usesr"(?u)\b\w+\b", including single-character words (e.g.,"a","C") in BM25 scoring. Previously, the regexr"(?u)\b\w\w+\b"excluded these tokens. This change may slightly alter retrieval results. To restore the old behavior, explicitly pass the previous regex when initializing the document store.
💙 Big thank you to everyone who contributed to this release!
@aayushbaluni, @anakin87, @bilgeyucel, @bogdankostic, @Br1an67, @ComeOnOliver, @davidsbatista, @jnMetaCode, @julian-risch, @Krishnachaitanyakc, @maxdswain, @pandego, @RMartinWhozfoxy, @satishkc7, @sjrl, @srini047, @SyedShahmeerAli12, @v-tan, @xr843
v2.27.0-rc1
v2.27.0-rc1
v2.26.1
Security Notes
- Fixed an issue in
ChatPromptBuilderwhere specially crafted template variables could be interpreted as structured content (e.g., images, tool calls) instead of plain text. Template variables are now automatically sanitized during rendering, ensuring they are always treated as plain text.
🐛 Bug Fixes
- Fix malformed log format string in
DocumentCleaner. The warning for documents withNonecontent used%{document_id}instead of{document_id}, preventing proper interpolation of the document ID.
v2.26.1-rc1
v2.26.1-rc1
v2.26.0
⭐ Highlights
🧠 More Flexible Agents with Dynamic System Prompts
Agent now supports Jinja2 templating in system_prompt, enabling runtime parameter injection and conditional logic directly in system messages. This makes it easier to adapt agent behavior dynamically (e.g. language, tone, time-aware responses) and reuse agents across contexts without redefining prompts
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
agent = Agent(
chat_generator=OpenAIChatGenerator(),
tools=[weather_tool],
system_prompt="""{% message role='system' %}
You always respond in {{language}}.
{% endmessage %}""",
required_variables=["language"],
)
result = agent.run(
messages=[ChatMessage.from_user("What is the weather in London?")],
language="Italian" # required variable for the system prompt
)
print(result["last_message"].text)
# >> Il tempo a Londra è soleggiato.🔍 LLMRanker for LLM-Based Reranking
LLMRanker introduces LLM-powered reranking, treating relevance as a semantic reasoning task rather than similarity scoring. This can yield better results for complex or multi-step queries compared to cross-encoders. The component can also filter out irrelevant/duplicate documents entirely, helping provide higher-quality context in RAG pipelines and agent workflows while keeping context windows lean.
from haystack import Document
from haystack.components.rankers import LLMRanker
ranker = LLMRanker()
documents = [
Document(id="paris", content="Paris is the capital of France."),
Document(id="berlin", content="Berlin is the capital of Germany."),
]
result = ranker.run(query="capital of Germany", documents=documents)
print(result["documents"][0].id) # "berlin"📦 Discover Supported Models Programmatically
OpenAIChatGenerator, OpenAIResponsesChatGenerator, and AzureOpenAIResponsesChatGenerator now expose a SUPPORTED_MODELS class variable, giving you the list of models supported by each component.
from haystack.components.generators.chat import OpenAIChatGenerator
print(OpenAIChatGenerator.SUPPORTED_MODELS)Note: We’ll roll out this pattern to other ChatGenerator components step by step. See issue #10627 for progress.
🚀 New Features
-
Added
LLMRanker, a new ranker component that uses aChatGeneratorandPromptBuilderto rerank documents based on JSON-formatted LLM output.LLMRankersupports configurable prompts, optional custom chat generators, runtimetop_koverrides, and serialization. -
AzureOpenAIResponsesChatGeneratorexposes aSUPPORTED_MODELSclass variable listing supported model IDs, for examplegpt-5-miniandgpt-4o. To view all supported models go to the [API reference](https://docs.haystack.deepset.ai/reference/generators-api#azureopenairesponseschatgenerator) or run:from haystack.components.generators.chat import AzureOpenAIResponsesChatGenerator print(AzureOpenAIResponsesChatGenerator.SUPPORTED_MODELS)
-
We now allow a component's whose input type is typed as a union of lists (e.g.
list[str] | list[ChatMessage]) to allow multiple input connections. Previously we only supported bare lists (e.g.list[str]) or optional lists (e.g.list[str] | None) to allow multiple input connections. A common use case for this is using theAnswerBuildercomponent which has it'srepliesinput typed aslist[str] | list[ChatMessage]. -
The
system_promptinitialization parameter of theAgentcomponent now supports Jinja2 message template syntax. This allows you to define the template at initialization time and pass runtime variables when calling therunmethod. This can be useful to inject dynamic values (such as the current time) or to add conditional instructions.Example usage:
from haystack.components.agents import Agent from haystack.components.generators.chat import OpenAIChatGenerator from haystack.dataclasses import ChatMessage from haystack.tools import tool @tool def weather(location: str) -> str: return f"The weather in {location} is sunny." agent = Agent( chat_generator=OpenAIChatGenerator(), tools=[weather], system_prompt="""{% message role='system' %} You always respond in {{language}}. {% endmessage %}""", required_variables=["language"], ) messages = [ChatMessage.from_user("What is the weather in London?")] result = agent.run(messages=messages, language="Italian") print(result["last_message"].text) # >> Il tempo a Londra è soleggiato.
-
OpenAIChatGeneratorexposes aSUPPORTED_MODELSclass variable listing supported model IDs, for examplegpt-5-miniandgpt-4o. To view all supported models go to the [API reference](https://docs.haystack.deepset.ai/reference/generators-api#openaichatgenerator) or run:from haystack.components.generators.chat import OpenAIChatGenerator print(OpenAIChatGenerator.SUPPORTED_MODELS)
⚡️Enhancement Notes
-
SearchableToolsetnow supports customizing the bootstrap search tool's name, description, and parameter descriptions via three new optional__init__parameters:search_tool_name,search_tool_description, andsearch_tool_parameters_description. This allows users to tune the LLM-facing metadata to work better with different models.Example usage:
from haystack.tools import SearchableToolset toolset = SearchableToolset( catalog=my_tools, search_tool_name="find_tools", search_tool_description="Find tools by keyword. Pass 1-3 words, not sentences.", search_tool_parameters_description={ "tool_keywords": "Single words only, e.g. 'hotel booking'.", }, )
-
Add support for python 3.14 to Haystack. Haystack already mostly supported python 3.14. Only minor changes were needed in regards to our type serialization and type checking when handling bare Union types.
-
Make the runtime parameter
messagestoAgentmessages optional since it is possible to execute the agent with only providing auser_prompt. -
Improve performance of
HuggingFaceAPIDocumentEmbedder.run_asyncby requesting embedding inference concurrently. This can be controlled using the newconcurrency_limitparameter. -
Removed redundant deepcopy operations from
PipelineandAsyncPipelineexecution. Component outputs are no longer deepcopied when collecting pipeline results, as inputs are already deepcopied before each component executes, preventing unintended mutations. Component inputs and outputs are also no longer deepcopied before being stored in tracing spans. These changes improve pipeline execution performance, especially when large objects (e.g., lists of Documents) flow between components and wheninclude_outputs_fromis used. -
Reduced unnecessary deepcopies in Agent for improved performance. Replaced deepcopy of
state_schemawith a shallow dict copy since only top-level keys are modified, and removed deepcopy ofagent_inputsfor span tags since the dict is freshly created and only used for tracing. -
Enable async embedding-based splitting with a new
run_asyncmethod onEmbeddingBasedDocumentSplitter. -
Added gpt-5.4 to OpenAIChatGenerator's list of supported models.
-
Added runtime validation of component output keys in
PipelineandAsyncPipeline. When a component returns keys that were not declared in its@component.output_types, the pipeline now logs a warning identifying the misconfigured component. This helps diagnose issues where a component returns unexpected keys, which previously caused a confusing "Pipeline Blocked" error pointing to an unexpected (downstream) component.
🐛 Bug Fixes
-
Fixed
Agent.run_asyncto mirrorAgent.runcrash handling for internalchat_generatorandtool_invokerfailures. Async runs now wrap internalPipelineRuntimeErrorexceptions with Agent context and attach pipeline snapshots so standalone async failures can be debugged and resumed consistently. -
Fixed ToolBreakpoint validation in
Agent.runandAgent.run_asyncto validate against tools selected for the current run. This allows breakpoints for runtime tool overrides to work correctly. -
Replaced in-place dataclass attribute mutation with
dataclasses.replace()across multiple components to prevent unintended side-effects when the same dataclass instance is shared across pipeline branches.Affected components and dataclasses:
ChatPromptBuilderandDynamicChatPromptBuilder:ChatMessage._contentHuggingFaceLocalChatGenerator:ChatMessage._contentHuggingFaceTEIRanker:Document.scoreMetaFieldRanker:Document.scoreSentenceTransformersSimilarityRanker:Document.scoreTransformersSimilarityRanker:Document.scoreExtractiveReader:ExtractedAnswer.queryInMemoryDocumentStore:Document.embedding
-
Update
Pipeline.inputs()to return any variadic inputs as not mandatory if they already have a connection. Removed the utility functionsdescribe_pipeline_inputsanddescribe_pipeline_inputs_as_stringfromhaystack/core/pipeline/descriptions.pysince they were not used and not referenced in the documentation. Use thePipeline.inputs()method to inspect the inputs of a pipeline. -
Fixed a bug in the pipeline scheduling logic where a component with all-optional inputs (e.g.
Agentafter makingmessagesoptional) could be scheduled ahead of a variadic joiner (e.g.ListJoiner) that was still waiting on inputs. The fix updates the tiebreaking logic in `_tiebreak_wai...
v2.26.0-rc1
v2.26.0-rc1
v2.25.2
🐛 Bug Fixes
- Reverts the change that made Agent messages optional as it caused issues with pipeline execution. As a consequence, the LLM component now defaults to an empty messages list unless provided at runtime.
v2.25.2-rc1
v2.25.2-rc1
v2.25.1
⚡️ Enhancement Notes
- Auto variadic sockets now also support
Optional[list[...]]input types, in addition to plainlist[...].
🐛 Bug Fixes
- Fixed smart connection logic to support connecting multiple outputs to a socket whose type is
Optional[list[...]](e.g.list[ChatMessage] | None). Previously, connecting twolist[ChatMessage]outputs toAgent.messageswould fail after its type was updated fromlist[ChatMessage]tolist[ChatMessage] | None.
v2.25.1-rc1
v2.25.1-rc1