Haystack
v3.1.0 open-sourceOpen-source AI orchestration framework for building context-engineered, production-ready LLM applications. Design modular pipelines and agent workflows with explicit control over retrieval, routing, memory, and generation. Built for scalable agents, RAG, multimodal applications, semantic search, and conversational systems.
from haystack.hooks.compaction import CompactionHook, SlidingWindowCompactor
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIResponsesChatGenerator
hook = CompactionHook(
compactor=SlidingWindowCompactor(),
context_window=128_000,
compact_at=0.8,
compact_to=0.5,
)
agent = Agent(
chat_generator=OpenAIResponsesChatGenerator(model="gpt-4o"),
tools=[web_search],
hooks={"before_llm": [hook]},
)
result = agent.run([ChatMessage.from_user("Summarize recent AI news.")])
from haystack.token_counters import ApproximateTokenCounter, TiktokenCounter
from haystack.dataclasses import ChatMessage
messages = [ChatMessage.from_user("Explain quantum entanglement.")]
# No extra install required
approx_count = ApproximateTokenCounter(chars_per_token=4.0).count(messages)
# Closer estimate for OpenAI models — requires: pip install tiktoken
tiktoken_count = TiktokenCounter(encoding="o200k_base").count(messages)
print(f"Approximate: {approx_count}, Tiktoken: {tiktoken_count}")
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIResponsesChatGenerator
from haystack.dataclasses import ChatMessage
from haystack.tools import AgentTool
researcher = Agent(
chat_generator=OpenAIResponsesChatGenerator(model="gpt-4o-mini"),
system_prompt="You are a research specialist. Investigate the task and report your findings.",
tools=[web_search],
)
research_specialist = AgentTool(
agent=researcher,
name="research",
description="Research a question on the web and report the findings",
)
coordinator = Agent(
chat_generator=OpenAIResponsesChatGenerator(model="gpt-4o"),
tools=[research_specialist],
system_prompt="You coordinate specialists. Delegate research questions, then answer the user.",
)
result = coordinator.run([ChatMessage.from_user("What are the latest LLM benchmarks?")])
print(result["last_message"].text)
from haystack.dataclasses import ChatMessage
from haystack.token_counters import OpenAITokenCounter
counter = OpenAITokenCounter("gpt-5-mini")
count = counter.count([ChatMessage.from_user("Summarize the quarterly report.")])
print(count)
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIResponsesChatGenerator
from haystack.hooks.compaction import CompactionHook, SlidingWindowCompactor
hook = CompactionHook(
compactor=SlidingWindowCompactor(),
context_window=400_000,
compact_at=0.7,
compact_to=0.4,
)
agent = Agent(
chat_generator=OpenAIResponsesChatGenerator(model="gpt-5.4-nano"),
tools=[web_search],
hooks={"before_llm": [hook]},
)
export HAYSTACK_UNSAFE_DESERIALIZATION=1
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIResponsesChatGenerator
from haystack.hooks.compaction import CompactionHook, SlidingWindowCompactor
hook = CompactionHook(
compactor=SlidingWindowCompactor(),
context_window=400_000,
compact_at=0.7,
compact_to=0.4,
)
agent = Agent(
chat_generator=OpenAIResponsesChatGenerator(model="gpt-5.4-nano"),
tools=[web_search],
hooks={"before_llm": [hook]},
)
from haystack.dataclasses import ChatMessage
from haystack.token_counters import OpenAITokenCounter
counter = OpenAITokenCounter("gpt-5-mini")
count = counter.count([ChatMessage.from_user("Hello!")])
print(count)
HAYSTACK_UNSAFE_DESERIALIZATION=1 python my_pipeline_server.py
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIResponsesChatGenerator
from haystack.hooks.compaction import CompactionHook, SlidingWindowCompactor
hook = CompactionHook(
compactor=SlidingWindowCompactor(),
context_window=400_000,
compact_at=0.7,
compact_to=0.4,
)
agent = Agent(
chat_generator=OpenAIResponsesChatGenerator(model="gpt-4o"),
tools=[web_search],
hooks={"before_llm": [hook]},
)
from haystack.dataclasses import ChatMessage
from haystack.token_counters import TiktokenCounter
counter = TiktokenCounter(encoding="o200k_base")
messages = [ChatMessage.from_user("Summarize the last 10 CVEs.")]
token_count = counter.count(messages, tools=[my_tool])
print(token_count)
from haystack.components.agents import Agent
from haystack.components.routers import ConditionalRouter
# Agent now includes exit_reason in its output
result = agent.run(messages=[ChatMessage.from_user("Research CVE-2024-1234.")])
print(result["exit_reason"]) # 'text', 'max_agent_steps', or a tool name
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.hooks import hook
@hook
def audit_tool_calls(state):
pending = state.data['messages'][-1].tool_calls
print(f'about to run: {[tc.tool_name for tc in pending]}')
agent = Agent(
chat_generator=OpenAIChatGenerator(),
tools=[...],
hooks={'before_tool': [audit_tool_calls]},
)
result = agent.run(messages=[{'role': 'user', 'content': 'Summarize recent alerts'}])
from haystack import Pipeline
with open('pipeline.yaml') as fp:
pipeline = Pipeline.load(fp, allowed_modules=['mypkg.*'])
from haystack.components.routers import ConditionalRouter
routes = [
{
"condition": "{{query.intent == 'search'}}",
"output": "query",
"output_name": "search_query",
"output_type": ParsedQuery,
"output_passthrough": True,
},
]
router = ConditionalRouter(routes)
result = router.run(query=ParsedQuery(text="What is Haystack?", intent="search", entities=[]))
assert result["search_query"].intent == "search" # type preserved
from haystack.components.evaluators import FaithfulnessEvaluator
import asyncio
evaluator = FaithfulnessEvaluator()
result = await evaluator.run_async(
questions=["What is Haystack?"],
contexts=[["Haystack is an AI framework."]],
predicted_answers=["Haystack is an AI framework."]
)
from haystack.components.generators.chat import AzureOpenAIChatGenerator
from haystack.utils import Secret
generator = AzureOpenAIChatGenerator(
azure_deployment="gpt-4o",
azure_endpoint=Secret.from_env_var("AZURE_OPENAI_ENDPOINT"),
api_version=Secret.from_env_var("AZURE_OPENAI_API_VERSION"),
)
from haystack.components.preprocessors import PythonCodeSplitter
splitter = PythonCodeSplitter(
max_effective_lines=80,
strip_docstrings=True,
preserve_class_definition=True,
)
result = splitter.run(documents=[doc])
for chunk in result["documents"]:
print(chunk.meta["start_line"], chunk.meta["unit_kinds"], chunk.content[:120])
from haystack.components.generators.chat import OpenAIChatGenerator
generator = OpenAIChatGenerator()
response = generator.run("Summarize the OWASP Top 10 in three sentences.")
print(response["replies"][0].text)
from haystack.components.retrievers import MultiRetriever, TextEmbeddingRetriever
from haystack.components.retrievers.in_memory import InMemoryBM25Retriever, InMemoryEmbeddingRetriever
from haystack.components.embedders import SentenceTransformersTextEmbedder
retriever = MultiRetriever(
retrievers={
"bm25": InMemoryBM25Retriever(document_store=doc_store),
"embedding": TextEmbeddingRetriever(
retriever=InMemoryEmbeddingRetriever(document_store=doc_store),
text_embedder=SentenceTransformersTextEmbedder(model="sentence-transformers/all-MiniLM-L6-v2"),
),
},
top_k=3,
)
# Full hybrid search
result = retriever.run(query="green energy sources")
# BM25 only for short/keyword queries
result = retriever.run(query="solar", active_retrievers=["bm25"])
retriever = MultiRetriever(
retrievers={
"bm25": InMemoryBM25Retriever(document_store=doc_store),
"embedding": TextEmbeddingRetriever(
retriever=InMemoryEmbeddingRetriever(document_store=doc_store),
text_embedder=SentenceTransformersTextEmbedder(model="sentence-transformers/all-MiniLM-L6-v2"),
),
},
join_mode="concatenate",
top_k=5,
)
from haystack.components.preprocessors import MarkdownHeaderSplitter
splitter = MarkdownHeaderSplitter(header_split_levels=[1, 2], keep_headers=True)
result = splitter.run(documents=[document])["documents"]
from haystack.components.agents import State
from haystack.tools import tool
@tool
def my_tool(query: str, state: State) -> str:
"""Search using context from agent state."""
history = state.get("history")
...
from haystack.document_stores.in_memory import InMemoryDocumentStore
store = InMemoryDocumentStore()
# ... write documents ...
print(store.get_metadata_field_min_max("year"))
print(store.get_metadata_field_unique_values("category"))
print(store.count_documents_by_filter({"field": "meta.category", "operator": "==", "value": "finance"}))
from haystack.components.generators.chat import AzureOpenAIChatGenerator
print(AzureOpenAIChatGenerator.SUPPORTED_MODELS)
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"
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",
)
print(result["last_message"].text)
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'.",
},
)
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
from haystack.tools import Tool, SearchableToolset
catalog = [
Tool(name="get_weather", description="Get weather for a city"),
Tool(name="search_web", description="Search the web"),
# ... hundreds more tools
]
toolset = SearchableToolset(catalog=catalog)
agent = Agent(chat_generator=OpenAIChatGenerator(), tools=toolset)
result = agent.run(messages=[ChatMessage.from_user("What's the weather in Milan?")])
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIChatGenerator
agent = Agent(
chat_generator=OpenAIChatGenerator(),
system_prompt="You are a helpful translation assistant.",
user_prompt="""{% message role="user"%}
Translate the following document to {{ language }}: {{ document }}
{% endmessage %}""",
required_variables=["language", "document"],
)
result = agent.run(language="French", document="The weather is lovely today.")
from haystack.components.generators.chat import LLM, OpenAIChatGenerator
llm = LLM(
chat_generator=OpenAIChatGenerator(),
system_prompt="You are a helpful assistant.",
user_prompt="""{% message role="user"%}
Summarize the following document: {{ document }}
{% endmessage %}""",
required_variables=["document"],
)
result = llm.run(document="Haystack v2.25.0 introduces SearchableToolset and a new LLM component.")
print(result["last_message"].text)
from haystack.components.generators.chat.openai import OpenAIChatGenerator
from haystack.dataclasses.chat_message import ChatMessage
from haystack.dataclasses.file_content import FileContent
file_content = FileContent.from_url("https://arxiv.org/pdf/2309.08632")
chat_message = ChatMessage.from_user(content_parts=[file_content, "Summarize this paper in 100 words."])
llm = OpenAIChatGenerator(model="gpt-4.1-mini")
response = llm.run(messages=[chat_message])
from haystack import Pipeline
from haystack.components.converters import HTMLToDocument, TextFileToDocument
from haystack.components.routers import FileTypeRouter
from haystack.components.writers import DocumentWriter
from haystack.dataclasses import ByteStream
from haystack.document_stores.in_memory import InMemoryDocumentStore
doc_store = InMemoryDocumentStore()
pipe = Pipeline()
pipe.add_component("router", FileTypeRouter(mime_types=["text/plain", "text/html"]))
pipe.add_component("txt_converter", TextFileToDocument())
pipe.add_component("html_converter", HTMLToDocument())
pipe.add_component("writer", DocumentWriter(doc_store))
pipe.connect("router.text/plain", "txt_converter.sources")
pipe.connect("router.text/html", "html_converter.sources")
pipe.connect("txt_converter.documents", "writer.documents")
pipe.connect("html_converter.documents", "writer.documents")
from haystack import Pipeline
from haystack.components.builders import ChatPromptBuilder
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.components.retrievers import InMemoryBM25Retriever
from haystack.document_stores.in_memory import InMemoryDocumentStore
p = Pipeline()
p.add_component("prompt_builder", ChatPromptBuilder(template=template))
p.add_component("llm", OpenAIChatGenerator(model="gpt-4.1-mini"))
p.add_component("retriever", InMemoryBM25Retriever(document_store=document_store, top_k=3))
# list[ChatMessage] from llm is auto-converted to str for retriever
p.connect("prompt_builder", "llm")
p.connect("llm", "retriever")
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIResponsesChatGenerator
from haystack.tools import ComponentTool
from haystack.dataclasses import ChatMessage, ImageContent
from haystack import component
@component
class ImageRetriever:
@component.output_types(images=list[ImageContent])
def run(self):
return {"images": [ImageContent.from_file_path("/data/image.jpg")]}
image_tool = ComponentTool(
component=ImageRetriever(),
outputs_to_string={"raw_result": True, "source": "images"}
)
agent = Agent(
chat_generator=OpenAIResponsesChatGenerator(model="gpt-5-nano"),
system_prompt="Retrieve images and describe them.",
tools=[image_tool],
)
result = agent.run(messages=[ChatMessage.from_user("Retrieve the image and describe it.")])
print(result["last_message"].text)
import json
def save_to_db(snapshot: dict) -> None:
db.snapshots.insert_one({"data": json.dumps(snapshot)})
result = pipeline.run(
data={"query": "What is RAG?"},
snapshot_callback=save_to_db
)
from haystack.components.embedders import SentenceTransformersDocumentEmbedder
from haystack.components.preprocessors import EmbeddingBasedDocumentSplitter
embedder = SentenceTransformersDocumentEmbedder()
splitter = EmbeddingBasedDocumentSplitter(
document_embedder=embedder,
sentences_per_group=2,
percentile=0.95,
min_length=50,
max_length=1000
)
result = splitter.run(documents=[doc])
from haystack.tools import Tool
tool = Tool(
name="search",
description="Search for documents",
parameters={...},
function=search_func,
outputs_to_string={
"formatted_docs": {"source": "documents", "handler": format_documents},
"summary": {"source": "metadata", "handler": format_summary}
# 'debug_info' is omitted and will not be stringified
}
)
from haystack.components.rankers.sentence_transformers_similarity import SentenceTransformersSimilarityRanker
ranker = SentenceTransformersSimilarityRanker(
model="tomaarsen/Qwen3-Reranker-0.6B-seq-cls",
query_prefix='<|im_start|>system\nJudge whether the Document meets the requirements...\n<Query>: ',
query_suffix="\n",
document_prefix="<Document>: ",
document_suffix="<|im_end|>\n<|im_start|>assistant\n<think>\n\n</think>\n\n",
)
result = ranker.run(query="Which planet is known as the Red Planet?", documents=[...])
from haystack.components.query import QueryExpander
from haystack.components.retrievers import InMemoryBM25Retriever, MultiQueryTextRetriever
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.components.writers import DocumentWriter
from haystack import Document
from haystack.document_stores.types import DuplicatePolicy
store = InMemoryDocumentStore()
writer = DocumentWriter(document_store=store, policy=DuplicatePolicy.SKIP)
writer.run(documents=[Document(content="Renewable energy comes from wind and sunlight.")])
expander = QueryExpander()
retriever = InMemoryBM25Retriever(document_store=store, top_k=3)
multi_retriever = MultiQueryTextRetriever(retriever=retriever)
expanded = expander.run(query="renewable energy")
results = multi_retriever.run(queries=expanded["queries"])
for doc in results["documents"]:
print(doc.content)
from haystack.components.generators.chat import OpenAIResponsesChatGenerator
from haystack.dataclasses import ChatMessage
chat_generator = OpenAIResponsesChatGenerator(
model="o3-mini",
generation_kwargs={"summary": "auto", "effort": "low"},
tools=[{"type": "web_search"}],
)
response = chat_generator.run(messages=[ChatMessage.from_user("What's a positive news story from today?")])
print(response["replies"][0].text)
from haystack.components.generators.chat.fallback import FallbackChatGenerator
from haystack.components.generators.chat.openai import OpenAIChatGenerator
from haystack_integrations.components.generators.anthropic import AnthropicChatGenerator
from haystack.dataclasses import ChatMessage
chat_generator = FallbackChatGenerator(chat_generators=[
AnthropicChatGenerator(model="claude-sonnet-4-5", timeout=5),
OpenAIChatGenerator(model="gpt-4o-mini"),
])
response = chat_generator.run(messages=[ChatMessage.from_user("Summarize the OWASP Top 10.")])
print(response["meta"]["successful_chat_generator_class"])
print(response["replies"][0].text)
from haystack.components.embedders import SentenceTransformersSparseTextEmbedder
embedder = SentenceTransformersSparseTextEmbedder()
embedder.warm_up()
result = embedder.run("Detect lateral movement via SMB.")
print(result["sparse_embedding"]) # SparseEmbedding(indices=[...], values=[...])
from haystack.components.agents import Agent
from haystack.tools import Tool, Toolset
agent = Agent(
chat_generator=generator,
tools=[math_toolset, weather_toolset, calendar_tool],
)
# At runtime, restrict to only the tools needed for this task
response = agent.run(
messages=[ChatMessage.from_user("What is 42 * 7?")],
tools=["multiply"],
)
try:
pipeline.run(data=input_data)
except PipelineRuntimeError as exc_info:
snapshot = exc_info.value.pipeline_snapshot
intermediate_outputs = snapshot.pipeline_state.pipeline_outputs
# inspect outputs, fix the issue, then resume
pipeline.run(data={}, snapshot=snapshot)
from haystack import Pipeline
from haystack.tools import PipelineTool
retrieval_pipeline = Pipeline()
# ... add components ...
retrieval_tool = PipelineTool(
pipeline=retrieval_pipeline,
input_mapping={"query": ["bm25_retriever.query"]},
output_mapping={"ranker.documents": "documents"},
name="retrieval_tool",
description="Use to retrieve documents",
)
from pydantic import BaseModel
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
class CalendarEvent(BaseModel):
event_name: str
event_date: str
event_location: str
generator = OpenAIChatGenerator(
model="gpt-4o-2024-08-06",
generation_kwargs={"response_format": CalendarEvent}
)
result = generator.run([ChatMessage.from_user("The Open NLP Meetup is in Berlin on September 19.")])
print(result["replies"][0].text)
from haystack.dataclasses import ChatMessage
msg = ChatMessage.from_assistant(
text="The answer is 42.",
reasoning="First I considered the problem domain, then narrowed down..."
)
print(msg.reasoning)
tool_invoker.run(
messages=chat_history,
tools=[search_tool, calculator_tool] # overrides constructor tools
)
from haystack.dataclasses.breakpoints import AgentBreakpoint, Breakpoint
from haystack.dataclasses import ChatMessage
chat_generator_breakpoint = Breakpoint(
component_name="chat_generator",
visit_count=0,
snapshot_file_path="debug_snapshots"
)
agent_breakpoint = AgentBreakpoint(break_point=chat_generator_breakpoint, agent_name="calculator_agent")
response = agent.run(
messages=[ChatMessage.from_user("What is 7 * (4 + 2)?")],
break_point=agent_breakpoint
)
from haystack.dataclasses import ImageContent, ChatMessage
from haystack.components.generators.chat import OpenAIChatGenerator
image_content = ImageContent.from_url("https://cdn.britannica.com/79/191679-050-C7114D2B/Adult-capybara.jpg")
message = ChatMessage.from_user(
content_parts=["Describe the image in short.", image_content]
)
llm = OpenAIChatGenerator(model="gpt-4o-mini")
print(llm.run([message])["replies"][0].text)
from haystack.components.builders import ChatPromptBuilder
from haystack.dataclasses.chat_message import ImageContent
template = """
{% message role="user" %}
Hello! I am {{user_name}}.
What's the difference between the following images?
{% for image in images %}
{{ image | templatize_part }}
{% endfor %}
{% endmessage %}
"""
builder = ChatPromptBuilder(template=template)
result = builder.run(
user_name="John",
images=[
ImageContent.from_file_path("apple-fruit.jpg"),
ImageContent.from_file_path("apple-logo.jpg")
]
)
from haystack.components.generators.chat import HuggingFaceAPIChatGenerator
from haystack.components.routers.llm_messages_router import LLMMessagesRouter
from haystack.dataclasses import ChatMessage
chat_generator = HuggingFaceAPIChatGenerator(
api_type="serverless_inference_api",
api_params={"model": "meta-llama/Llama-Guard-4-12B", "provider": "groq"},
)
router = LLMMessagesRouter(
chat_generator=chat_generator,
output_names=["unsafe", "safe"],
output_patterns=["unsafe", "safe"],
)
print(router.run([ChatMessage.from_user("How to rob a bank?")]))
from haystack.components.embedders import OpenAIDocumentEmbedder
embedder = OpenAIDocumentEmbedder(raise_on_failure=True)
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.components.generators.utils import print_streaming_chunk
from haystack.tools import ComponentTool
from haystack.components.websearch import SerperDevWebSearch
from haystack.dataclasses import ChatMessage
web_search = ComponentTool(name="web_search", component=SerperDevWebSearch(top_k=5))
agent = Agent(
chat_generator=OpenAIChatGenerator(model="gpt-4o-mini"),
tools=[web_search],
streaming_callback=print_streaming_chunk
)
result = agent.run(messages=[ChatMessage.from_user("What happened in AI news today?")])
print(result["last_message"].text)
from haystack.components.rankers import SentenceTransformersSimilarityRanker
from haystack.utils.device import ComponentDevice
from haystack.dataclasses import Document
ranker = SentenceTransformersSimilarityRanker(
model="sentence-transformers/all-MiniLM-L6-v2",
device=ComponentDevice.from_str("cpu"),
backend="onnx",
)
ranker.warm_up()
docs = [Document(content="Berlin"), Document(content="Sarajevo")]
output = ranker.run(query="City in Germany", documents=docs)
print(output["documents"])
from pathlib import Path
from haystack import Pipeline
from haystack.components.converters import MultiFileConverter
from haystack.components.preprocessors import DocumentPreprocessor
pipeline = Pipeline()
pipeline.add_component("converter", MultiFileConverter())
pipeline.add_component("preprocessor", DocumentPreprocessor())
pipeline.connect("converter", "preprocessor")
pipeline.draw(path=Path("expanded_pipeline.png"), super_component_expansion=True)
result = await web_search_agent.run_async(
messages=[ChatMessage.from_user("Find information about Haystack by deepset")]
)
from haystack.tools import Toolset
from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIChatGenerator
math_toolset = Toolset([tool_one, tool_two])
agent = Agent(
chat_generator=OpenAIChatGenerator(model="gpt-4o-mini"),
tools=math_toolset
)
from haystack import Pipeline, super_component
from haystack.components.joiners import DocumentJoiner
from haystack.components.embedders import SentenceTransformersTextEmbedder
from haystack.components.retrievers import InMemoryBM25Retriever, InMemoryEmbeddingRetriever
from haystack.document_stores.in_memory import InMemoryDocumentStore
@super_component
class HybridRetriever:
def __init__(self, document_store: InMemoryDocumentStore):
self.pipeline = Pipeline()
self.pipeline.add_component("text_embedder", SentenceTransformersTextEmbedder())
self.pipeline.add_component("embedding_retriever", InMemoryEmbeddingRetriever(document_store))
self.pipeline.add_component("bm25_retriever", InMemoryBM25Retriever(document_store))
self.pipeline.add_component("document_joiner", DocumentJoiner(join_mode="reciprocal_rank_fusion"))
self.pipeline.connect("text_embedder", "embedding_retriever")
self.pipeline.connect("bm25_retriever", "document_joiner")
self.pipeline.connect("embedding_retriever", "document_joiner")
from haystack import Pipeline, SuperComponent
with open("rag_pipeline.yaml", "r") as f:
pipeline = Pipeline.load(f)
wrapper = SuperComponent(
pipeline=pipeline,
input_mapping={
"query": ["retriever.query", "prompt_builder.query"],
},
output_mapping={"llm.replies": "replies"},
)
result = wrapper.run(query="What is the capital of France?")
print(result["replies"])
from haystack.components.preprocessors import CSVDocumentSplitter
splitter = CSVDocumentSplitter(split_mode="row-wise")
result = splitter.run(documents=docs)
from haystack.components.converters import MSGToDocument
converter = MSGToDocument()
result = converter.run(sources=["email.msg"])
print(result["documents"][0].meta) # sender, recipients, subject, etc.
print(result["bytestream_outputs"]) # attachments as ByteStream objects
from haystack import Pipeline
pipeline = Pipeline(connection_type_validation=False)
# Now connect Optional[str] -> str without a TypeError
pipeline.connect("component_a.optional_output", "component_b.str_input")
import asyncio
from haystack import AsyncPipeline
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
pipeline = AsyncPipeline()
pipeline.add_component("llm", OpenAIChatGenerator(model="gpt-4o"))
async def main():
result = await pipeline.run({"llm": {"messages": [ChatMessage.from_user("Hello")]}})
print(result)
asyncio.run(main())
from haystack.utils import Secret
from haystack.components.connectors.openapi import OpenAPIConnector
connector = OpenAPIConnector(
openapi_spec="https://bit.ly/serperdev_openapi",
credentials=Secret.from_env_var("SERPERDEV_API_KEY")
)
response = connector.run(operation_id="search", parameters={"q": "Who was Nikola Tesla?"})
from haystack import Pipeline
from haystack.tools import ComponentTool
from haystack.components.websearch import SerperDevWebSearch
from haystack.utils import Secret
from haystack.components.tools.tool_invoker import ToolInvoker
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage
search = SerperDevWebSearch(api_key=Secret.from_env_var("SERPERDEV_API_KEY"), top_k=3)
tool = ComponentTool(
component=search,
name="web_search",
description="Search the web for current information on any topic"
)
pipeline = Pipeline()
pipeline.add_component("llm", OpenAIChatGenerator(model="gpt-4o-mini", tools=[tool]))
pipeline.add_component("tool_invoker", ToolInvoker(tools=[tool]))
pipeline.connect("llm.replies", "tool_invoker.messages")
result = pipeline.run({"llm": {"messages": [ChatMessage.from_user("Who founded SpaceX?")]}})
print(result)
from haystack.components.generators import DALLEImageGenerator
image_generator = DALLEImageGenerator()
response = image_generator.run("Show me a picture of a black cat.")
print(response)
from haystack.components.builders import PromptBuilder
builder = PromptBuilder(
template="Summarize the following: {{ text }} in {{ language }}",
required_variables="*"
)
import logging
from haystack import tracing
from haystack.tracing.logging_tracer import LoggingTracer
logging.basicConfig(format="%(levelname)s - %(name)s - %(message)s", level=logging.WARNING)
logging.getLogger("haystack").setLevel(logging.DEBUG)
tracing.tracer.is_content_tracing_enabled = True
tracing.enable_tracing(LoggingTracer())
# Now run your pipeline — all spans appear in the log output
pipeline.run({"text_embedder": {"text": "What is RAG?"}})
from haystack.components.converters import JSONConverter
from haystack.dataclasses import ByteStream
import json
data = {"laureates": [{"firstname": "Enrico", "surname": "Fermi", "motivation": "discovery of nuclear reactions"}]}
source = ByteStream.from_string(json.dumps(data))
converter = JSONConverter(jq_schema=".laureates[]", content_key="motivation", extra_meta_fields=["firstname", "surname"])
results = converter.run(sources=[source])
print(results["documents"][0].content) # 'discovery of nuclear reactions'
print(results["documents"][0].meta) # {'firstname': 'Enrico', 'surname': 'Fermi'}
from haystack.components.preprocessors import DocumentSplitter
from haystack.dataclasses import Document
def split_on_headers(text: str) -> list[str]:
import re
return [s for s in re.split(r'(?=^#{1,3} )', text, flags=re.MULTILINE) if s.strip()]
splitter = DocumentSplitter(split_by="function", splitting_function=split_on_headers)
result = splitter.run(documents=[Document(content="# Intro\nHello\n## Details\nMore info")])
print([d.content for d in result["documents"]])
from haystack.components.routers import ConditionalRouter
router = ConditionalRouter(
routes=[
{"condition": "{{query | length > 50}}", "output": "{{chat_message}}", "output_type": "ChatMessage", "output_name": "long_query"}
],
unsafe=True
)
from haystack.components.samplers import TopPSampler
sampler = TopPSampler(p=0.90, min_top_k=3)
from haystack.components.evaluators import FaithfulnessEvaluator
evaluator = FaithfulnessEvaluator(
api_params={
"api_base_url": "http://localhost:11434/v1",
"generation_kwargs": {"temperature": 0.0, "max_tokens": 512},
}
)
result = evaluator.run(questions=["What is RAG?"], contexts=[["RAG combines retrieval and generation."]], responses=["RAG is a retrieval-augmented generation approach."])
from haystack.components.embedders import SentenceTransformersDocumentEmbedder
embedder = SentenceTransformersDocumentEmbedder(
model="sentence-transformers/all-MiniLM-L6-v2",
truncate_dim=128,
precision="int8",
)
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.dataclasses import Document
index = "shared_knowledge_base"
store_writer = InMemoryDocumentStore(index=index)
store_retriever = InMemoryDocumentStore(index=index)
store_writer.write_documents([Document(content="Haystack is an LLM framework.")])
print(store_retriever.count_documents()) # 1 — same memory
from haystack.components.rankers import MetaFieldRanker
ranker = MetaFieldRanker(meta_field="score", missing_meta="drop")
result = ranker.run(documents=docs)
print(result["documents"]) # only documents that have 'score' metadata
from haystack.components.retrievers.in_memory import InMemoryBM25Retriever
retriever = InMemoryBM25Retriever(
document_store=document_store,
filter_policy='merge'
)
prompt_node = PromptNode(
model_name_or_path='gpt-3.5-turbo',
api_key='<your-key>',
api_base='http://localhost:1234/v1'
)
converter = PDFToTextConverter(raise_on_failure=False)
docs = converter.convert(file_paths=my_file_list)
from haystack.nodes import PreProcessor
preprocessor = PreProcessor(split_by='page', split_length=1)
pages = preprocessor.process(documents)
export OPENAI_TIMEOUT=30
export OPENAI_MAX_RETRIES=5
python my_pipeline.py
from haystack.components.preprocessors import DocumentSplitter
splitter = DocumentSplitter(
split_by="word",
split_length=200,
split_threshold=50
)
from haystack.components.preprocessors import DocumentCleaner
cleaner = DocumentCleaner(keep_id=True)
pipe.run(data, include_outputs_from={"prompt_builder", "llm", "retriever"})
from haystack.components.evaluators import DocumentMAPEvaluator
from haystack import Document
evaluator = DocumentMAPEvaluator()
result = evaluator.run(
ground_truth_documents=[
[Document(content="France")],
[Document(content="9th century"), Document(content="9th")],
],
retrieved_documents=[
[Document(content="France")],
[Document(content="9th century"), Document(content="10th century"), Document(content="9th")],
],
)
print(result["score"]) # 0.9166666666666666
from haystack import Pipeline
from haystack_integrations.components.retrievers.qdrant import QdrantSparseEmbeddingRetriever
from haystack_integrations.components.embedders.fastembed import FastembedSparseTextEmbedder
sparse_text_embedder = FastembedSparseTextEmbedder(model="prithvida/Splade_PP_en_v1")
sparse_retriever = QdrantSparseEmbeddingRetriever(document_store=document_store)
query_pipeline = Pipeline()
query_pipeline.add_component("sparse_text_embedder", sparse_text_embedder)
query_pipeline.add_component("sparse_retriever", sparse_retriever)
query_pipeline.connect("sparse_text_embedder.sparse_embedding", "sparse_retriever.query_sparse_embedding")
pipe.run(data, include_outputs_from=["prompt_builder", "llm", "retriever"])
from haystack.components.evaluators import DocumentMAPEvaluator
evaluator = DocumentMAPEvaluator()
result = evaluator.run(
ground_truth_documents=[[Document(content="France")], [Document(content="9th century")]],
retrieved_documents=[[Document(content="France")], [Document(content="9th century"), Document(content="10th century")]],
)
print(result["score"])
from haystack import Pipeline
from haystack_integrations.components.retrievers.qdrant import QdrantSparseEmbeddingRetriever
from haystack_integrations.components.embedders.fastembed import FastembedSparseTextEmbedder
sparse_text_embedder = FastembedSparseTextEmbedder(model="prithvida/Splade_PP_en_v1")
sparse_retriever = QdrantSparseEmbeddingRetriever(document_store=document_store)
query_pipeline = Pipeline()
query_pipeline.add_component("sparse_text_embedder", sparse_text_embedder)
query_pipeline.add_component("sparse_retriever", sparse_retriever)
query_pipeline.connect("sparse_text_embedder.sparse_embedding", "sparse_retriever.query_sparse_embedding")
from haystack.components.routers import FileTypeRouter
from pathlib import Path
router = FileTypeRouter(mime_types=[r"text/.*", r"application/(pdf|json)"])
result = router.run(sources=[Path("report.pdf"), Path("notes.txt"), Path("data.json"), Path("image.png")])
for mime_type, files in result.items():
print(f"MIME Type: {mime_type}, Files: {[str(f) for f in files]}")
from haystack.components.evaluators import FaithfulnessEvaluator
evaluator = FaithfulnessEvaluator()
result = evaluator.run(
questions=["What is the capital of France?"],
contexts=[["Paris is the capital and largest city of France."]],
predicted_answers=["The capital of France is Paris."]
)
print(result["score"]) # float between 0 and 1
from haystack.components.generators import HuggingFaceLocalGenerator
def my_callback(token):
print(token, end="", flush=True)
generator = HuggingFaceLocalGenerator(
model="google/flan-t5-large",
streaming_callback=my_callback
)
generator.warm_up()
generator.run(prompt="Summarize the OWASP Top 10 in three sentences.")
from haystack import Pipeline
from haystack.components.fetchers import LinkContentFetcher
from haystack.components.converters import HTMLToDocument
from haystack.components.builders import PromptBuilder
from haystack.components.generators import OpenAIGenerator
from haystack.utils import Secret
fetcher = LinkContentFetcher()
converter = HTMLToDocument()
prompt_builder = PromptBuilder(template="""{% for document in documents %}{{document.content}}{% endfor %} Answer: {{query}}""")
llm = OpenAIGenerator(api_key=Secret.from_env_var("OPENAI_API_KEY"))
pipeline = Pipeline()
pipeline.add_component("fetcher", fetcher)
pipeline.add_component("converter", converter)
pipeline.add_component("prompt", prompt_builder)
pipeline.add_component("llm", llm)
pipeline.connect("fetcher.streams", "converter.sources")
pipeline.connect("converter.documents", "prompt.documents")
pipeline.connect("prompt.prompt", "llm.prompt")
pipeline.run({"fetcher": {"urls": ["https://haystack.deepset.ai/overview/quick-start"]}, "prompt": {"query": "How should I install Haystack?"}})
from haystack import Pipeline, PredefinedPipeline
pipeline = Pipeline.from_template(PredefinedPipeline.CHAT_WITH_WEBSITE)
pipeline.run({"fetcher": {"urls": ["https://haystack.deepset.ai/overview/quick-start"]}, "prompt": {"query": "How should I install Haystack?"}})
from haystack import component, Pipeline
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.components.retrievers.in_memory import InMemoryEmbeddingRetriever
import random
from typing import List
@component
class MyEmbedder:
def __init__(self, dim: int = 128):
self.dim = dim
@component.output_types(embedding=List[float])
def run(self, text: str):
return {"embedding": [random.uniform(-1.0, 1.0) for _ in range(self.dim)]}
document_store = InMemoryDocumentStore()
pipeline = Pipeline()
pipeline.add_component("text_embedder", MyEmbedder())
pipeline.add_component("retriever", InMemoryEmbeddingRetriever(document_store=document_store))
pipeline.connect("text_embedder.embedding", "retriever.query_embedding")
pipeline.run({"text_embedder": {"text": "Who lives in Berlin?"}})
preprocessor = PreProcessor(
split_by='page',
split_overlap=0
)
docs = preprocessor.process(raw_docs)
preprocessor = PreProcessor(split_by="page", split_length=1)
prompt_model = PromptModel(model_name_or_path="gpt-3.5-turbo", api_key="ignored", model_kwargs={"API_BASE": "http://localhost:1234/v1"})
converter = PDFToTextConverter(raise_on_failure=False)
from haystack.nodes import EmbeddingRetriever
retriever = EmbeddingRetriever(
embedding_model="amazon.titan-embed-text-v1",
document_store=document_store,
aws_config={
"aws_access_key_id": "ACCESS_KEY",
"aws_secret_access_key": "SECRET_KEY",
"aws_session_token": "SESSION_TOKEN"
}
)
from haystack.nodes import PromptNode
prompt_node = PromptNode(model_name_or_path="meta.llama2-13b-chat-v1")
from haystack.document_stores.mongodb_atlas import MongoDBAtlasDocumentStore
document_store = MongoDBAtlasDocumentStore(
mongo_connection_string="mongodb+srv://USER:PASSWORD@HOST/?retryWrites=true&w=majority",
database_name="my_database",
collection_name="my_collection",
)
document_store.write_documents(docs)
with open('pipeline.yaml', 'w') as f:
pipeline.dump(f)
retriever = WebRetriever(search_engine_kwargs={'engine': '<your-engine-id>'})
import asyncio
from haystack.nodes import PromptNode
pn = PromptNode(model_name_or_path="gpt-3.5-turbo", api_key="<your-key>")
async def main():
result = await pn.arun(prompt="Summarize the following text: <text>")
print(result)
asyncio.run(main())
pip install farm-haystack[preview]
from haystack.document_stores.pinecone import DOCUMENT_WITH_EMBEDDING
# Retrieve documents that have an embedding
docs_with_embedding = doc_store.get_all_documents(type_metadata=DOCUMENT_WITH_EMBEDDING)
# Retrieve documents without an embedding
docs_without_embedding = doc_store.get_all_documents(type_metadata="no-vector")
from haystack.nodes import WebRetriever, TopPSampler, DiversityRanker, LostInTheMiddleRanker
from haystack.pipelines import Pipeline
web_retriever = WebRetriever(api_key=search_key, top_search_results=5, mode="preprocessed_documents", top_k=50)
sampler = TopPSampler(top_p=0.97)
diversity_ranker = DiversityRanker()
litm_ranker = LostInTheMiddleRanker(word_count_threshold=1024)
pipeline = Pipeline()
pipeline.add_node(component=web_retriever, name="Retriever", inputs=["Query"])
pipeline.add_node(component=sampler, name="Sampler", inputs=["Retriever"])
pipeline.add_node(component=diversity_ranker, name="DiversityRanker", inputs=["Sampler"])
pipeline.add_node(component=litm_ranker, name="LostInTheMiddleRanker", inputs=["DiversityRanker"])
pipeline.add_node(component=prompt_node, name="PromptNode", inputs=["LostInTheMiddleRanker"])
retriever = BM25Retriever(
custom_query="""
{
"query": {
"bool": {
"should": [{"multi_match": {
"query": ${query},
"type": "most_fields",
"fields": ["content", "title"]}}],
"filter": ${filters}
}
}
}"""
)
retriever.retrieve(
query="What is the meaning of life?",
filters={"year": [2019, 2020], "quarter": [1, 2, 3], "date": {"$gte": "2019-03-01"}}
)
web_retriever = WebRetriever(
api_key=search_key,
allowed_domains=["docs.haystack.deepset.ai", "haystack.deepset.ai"],
top_search_results=10,
mode="preprocessed_documents"
)
pip install farm-haystack[elasticsearch8]
from haystack.nodes import PromptNode
prompt_node = PromptNode(
model_name_or_path="sagemaker-llama-2-chat-endpoint-name",
model_kwargs={
"aws_profile_name": "my_aws_profile_name",
"aws_custom_attributes": {"accept_eula": True}
}
)
chat = [[{"role": "user", "content": "Summarize CVE mitigations for Log4Shell."}]]
print(prompt_node(chat))
import os
from haystack.nodes import PromptNode, PromptTemplate
template = PromptTemplate("deepset/topic-classification")
prompt_node = PromptNode(
model_name_or_path="text-davinci-003",
api_key=os.environ.get("OPENAI_API_KEY")
)
result = prompt_node.prompt(
prompt_template=template,
documents="YOUR_DOCUMENTS",
options=["sports", "politics", "technology"]
)
from haystack.agents import Tool
from haystack.agents.conversational import ConversationalAgent
search_tool = Tool(
name="USA_Presidents_QA",
pipeline_or_node=presidents_qa_pipeline,
description="useful for when you need to answer questions about US presidents."
)
agent = ConversationalAgent(prompt_node=prompt_node, tools=[search_tool])
agent.run("Who was the 35th president of the United States?")
from haystack.agents.memory import ConversationalSummaryMemory
from haystack.agents import ConversationalAgent
summary_memory = ConversationalSummaryMemory(prompt_node=prompt_node)
agent = ConversationalAgent(prompt_node=prompt_node, memory=summary_memory)
response = agent.run(user_input="What are the main causes of climate change?")
pipeline.run(
query="Summarize this document",
params={
"PromptNode": {
"generation_kwargs": {"max_new_tokens": 200, "temperature": 0.7}
}
}
)
from haystack.nodes import PromptModel, PromptNode
prompt_model = PromptModel("gpt-4", api_key=api_key)
prompt_node = PromptNode(prompt_model)
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Summarize the attached document."},
]
result = prompt_node(messages)
web_qa_tool = Tool(
name="Search",
pipeline_or_node=WebQAPipeline(retriever=web_retriever, prompt_node=web_qa_pn),
description="useful for when you need to Google questions.",
output_variable="results",
)
agent = Agent(
prompt_node=agent_pn,
prompt_template=prompt_template,
tools=[web_qa_tool],
final_answer_pattern=r"Final Answer\s*:\s*(.*)",
)
agent.run(query="What is the capital of the country that won the 2022 FIFA World Cup?")
PromptTemplate(
name="question-answering",
prompt_text="Given the context please answer the question.\nContext: {join(documents)}\nQuestion: {query}\nAnswer: ",
output_parser=AnswerParser(),
)
prompt_model = PromptModel("gpt-3.5-turbo", api_key=api_key)
prompt_node = PromptNode(prompt_model)
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Who won the world series in 2020?"},
{"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."},
{"role": "user", "content": "Where was it played?"},
]
result = prompt_node(messages)
from haystack.nodes import PromptNode
node = PromptNode('gpt-3.5-turbo', model_kwargs={'temperature': 0.2, 'stop': ['\n']})
from haystack.document_stores import OpenSearchDocumentStore
store = OpenSearchDocumentStore(
index='my_index',
embedding_field='embedding',
embedding_dim=768,
ivf_train_size=10000
)
from haystack.nodes import PromptNode
pn = PromptNode(
model_name_or_path='text-davinci-003',
stop_words=['\nHuman:', 'END']
)
result = pn.run(prompt='Summarize the following document: ...')
from haystack.nodes import EmbeddingRetriever
# OpenAI
retriever = EmbeddingRetriever(
embedding_model="text-embedding-ada-002",
batch_size=32,
api_key=api_key,
max_seq_len=8191
)
# Cohere multilingual
retriever = EmbeddingRetriever(
embedding_model="multilingual-22-12",
batch_size=16,
api_key=api_key
)
from haystack.nodes import MarkdownConverter
converter = MarkdownConverter(extract_headlines=True)
docs = converter.convert(file_path="report.md", meta=None)
print(docs[0].meta['headlines'])
# [{'headline': 'Introduction', 'start_idx': 0, 'level': 1}, ...]
results = retriever.retrieve(
query="What is the capital of France?",
document_store=alternate_document_store
)
retriever = MultiModalRetriever(
document_store=InMemoryDocumentStore(embedding_dim=512),
query_embedding_model="sentence-transformers/clip-ViT-B-32",
query_type="text",
document_embedding_models={"image": "sentence-transformers/clip-ViT-B-32"}
)
retriever.train(
data_dir="training_data/",
train_filename="train.json",
loss_function="MultipleNegativesRankingLoss"
)
from haystack.nodes import FARMReader
from haystack.utils.early_stopping import EarlyStopping
reader = FARMReader(model_name_or_path="deepset/roberta-base-squad2-distilled")
reader.train(
data_dir="data/squad20",
train_filename="dev-v2.0.json",
early_stopping=EarlyStopping(min_delta=0.001),
use_gpu=True,
n_epochs=8,
save_dir="my_model"
)
from haystack.document_stores import OpenSearchDocumentStore
document_store = OpenSearchDocumentStore(knn_engine="faiss")
from haystack.nodes import TransformersQueryClassifier
classifier = TransformersQueryClassifier(
model_name_or_path="typeform/distilbert-base-uncased-mnli",
use_gpu=True,
task="zero-shot-classification",
labels=["music", "cinema", "food"],
)
result = classifier.run(query="Who directed Pulp Fiction?")
print(result)
pipelines:
- name: ray_query_pipeline
nodes:
- name: EmbeddingRetriever
replicas: 2
inputs: [ Query ]
serve_deployment_kwargs:
num_replicas: 2
version: Twenty
ray_actor_options:
num_gpus: 0.25
num_cpus: 0.5
max_concurrent_queries: 17
- name: Reader
inputs: [ EmbeddingRetriever ]
from haystack.nodes import FARMReader
reader = FARMReader(model_name_or_path="roberta-base")
reader.train(data_dir="my_squad_data", train_filename="squad2.json", n_epochs=1, save_dir="my_model")
reader.save_to_remote(repo_id="your-user-name/roberta-base-squad2", private=True, commit_message="First version of my qa model trained with Haystack")
from haystack.nodes import MultihopEmbeddingRetriever
from haystack.document_stores import InMemoryDocumentStore
document_store = InMemoryDocumentStore()
retriever = MultihopEmbeddingRetriever(
document_store=document_store,
embedding_model="deutschmann/mdr_roberta_q_encoder",
)
from pathlib import Path
from haystack.nodes import Text2SparqlRetriever
from haystack.document_stores import InMemoryKnowledgeGraph
kg = InMemoryKnowledgeGraph(index="tutorial10")
kg.create_index()
kg.import_from_ttl_file(index="tutorial10", path=Path("data/tutorial10/triples.ttl"))
kgqa_retriever = Text2SparqlRetriever(knowledge_graph=kg, model_name_or_path=Path("../saved_models/tutorial10/hp_v3.4"))
print(kgqa_retriever.retrieve(query="In which house is Harry Potter?"))
from haystack.nodes.retriever import EmbeddingRetriever
from haystack.document_stores import InMemoryDocumentStore
from haystack.nodes.question_generator.question_generator import QuestionGenerator
from haystack.nodes.label_generator.pseudo_label_generator import PseudoLabelGenerator
document_store = InMemoryDocumentStore()
document_store.write_documents([...])
retriever = EmbeddingRetriever(
document_store=document_store,
embedding_model="sentence-transformers/msmarco-distilbert-base-tas-b",
max_seq_len=200
)
document_store.update_embeddings(retriever)
qg = QuestionGenerator(model_name_or_path="doc2query/msmarco-t5-base-v1", max_length=64, split_length=200, batch_size=12)
psg = PseudoLabelGenerator(qg, retriever)
output, _ = psg.run(documents=document_store.get_all_documents())
retriever.train(output["gpl_labels"])
from haystack.pipelines import ExtractiveQAPipeline
pipe = ExtractiveQAPipeline(reader, retriever)
predictions = pipe.pipeline.run_batch(
queries=["Who is the father of Arya Stark?", "Who is the mother of Arya Stark?"],
params={"Retriever": {"top_k": 10}, "Reader": {"top_k": 5}}
)
eval_result = pipeline.eval(labels=eval_labels, params={"Retriever": {"top_k": 5}})
metrics = eval_result.calculate_metrics(answer_scope="context")
print(f'Reader - F1-Score: {metrics["Reader"]["f1"]}')
eval_result = Pipeline.execute_eval_run(
index_pipeline=index_pipeline,
query_pipeline=query_pipeline,
evaluation_set_labels=labels,
corpus_file_paths=file_paths,
corpus_file_metas=file_metas,
experiment_tracking_tool="mlflow",
experiment_tracking_uri="http://localhost:5000",
experiment_name="my-query-pipeline-experiment",
experiment_run_name="run_1",
pipeline_meta={"name": "my-pipeline-1"},
evaluation_set_meta={"name": "my-evalset"},
corpus_meta={"name": "my-corpus"},
add_isolated_node_eval=True,
reuse_index=False
)
from haystack.nodes import FARMReader
model = "deepset/roberta-base-squad2"
reader = FARMReader(model, confidence_threshold=0.5)
from pathlib import Path
from haystack.pipelines.config import validate_yaml
validate_yaml(Path('rest_api/pipeline/pipelines.haystack-pipeline.yml'))
import os
from haystack.document_stores import PineconeDocumentStore
document_store = PineconeDocumentStore(api_key=os.environ['PINECONE_API_KEY'])
from haystack.pipelines import DocumentSearchPipeline, Pipeline
from haystack.nodes import ElasticsearchRetriever
from haystack.document_stores.elasticsearch import ElasticsearchDocumentStore
document_store = ElasticsearchDocumentStore(search_fields=['content', 'name'], index='scifact_beir')
retriever = ElasticsearchRetriever(document_store=document_store, top_k=1000)
query_pipeline = DocumentSearchPipeline(retriever=retriever)
ndcg, _map, recall, precision = Pipeline.eval_beir(
index_pipeline=index_pipeline, query_pipeline=query_pipeline, dataset='scifact'
)
from haystack.document_stores import InMemoryDocumentStore
from haystack.utils import es_index_to_document_store
document_store = es_index_to_document_store(
document_store=InMemoryDocumentStore(),
original_index_name="existing_index",
original_content_field="content",
original_name_field="name",
included_metadata_fields=["date_field"],
index="new_index",
)
from haystack.nodes import TableReader
reader = TableReader(model_name_or_path="deepset/tapas-large-nq-reader", max_seq_len=512)
# Step 1: augment training data
python augment_squad.py --squad_path squad2.json --output_path augmented_squad2.json --multiplication_factor 20
# Step 2: distil intermediate layers
student.distil_intermediate_layers_from(teacher, data_dir="dataset", train_filename="augmented_squad2.json")
# Step 3: distil prediction layer
student.distil_prediction_layer_from(teacher, data_dir="dataset", train_filename="squad2.json")
eval_result = pipeline.eval(labels=eval_labels, add_isolated_node_eval=True)
pipeline.print_eval_report(eval_result)
from haystack.nodes import RCIReader
reader = RCIReader(
row_model_name_or_path="michaelrglass/albert-base-rci-wikisql-row",
column_model_name_or_path="michaelrglass/albert-base-rci-wikisql-col"
)
eval_result = pipeline.eval(
labels=labels,
params={"Retriever": {"top_k": 5}},
)
metrics = eval_result.calculate_metrics()
pipeline.print_eval_report(eval_result)
retriever = TableTextRetriever(
document_store=document_store,
query_embedding_model="deepset/bert-small-mm_retrieval-question_encoder",
passage_embedding_model="deepset/bert-small-mm_retrieval-passage_encoder",
table_embedding_model="deepset/bert-small-mm_retrieval-table_encoder",
embed_meta_fields=["title", "section_title"]
)
reader = TableReader(
model_name_or_path="google/tapas-base-finetuned-wtq",
max_seq_len=512
)
from haystack.pipeline import RayPipeline
pipeline = RayPipeline.load_from_yaml(path="my_pipelines.yaml", pipeline_name="ray_query_pipeline")
pipeline.run(query="What is the capital of Germany?")
from haystack.nodes import EvalAnswers
eval_reader = EvalAnswers(sas_model="sentence-transformers/paraphrase-multilingual-mpnet-base-v2")
from haystack.document_store import WeaviateDocumentStore
document_store = WeaviateDocumentStore()
document_store.write_documents(documents, duplicate_documents="overwrite")
curl -X POST http://localhost:8000/query \
-H 'Content-Type: application/json' \
-d '{"query": "Why did the revenue change?"}'
from haystack.pipeline import SearchSummarizationPipeline
from haystack.summarizer import TransformersSummarizer
summarizer = TransformersSummarizer(model_name_or_path="google/pegasus-xsum")
pipe = SearchSummarizationPipeline(summarizer=summarizer, retriever=retriever)
results = pipe.run(query="What caused the California wildfires?")
document_store.update_embeddings(retriever=retriever, batch_size=10000)
from haystack.pipeline import Pipeline, JoinDocuments
class QueryClassifier:
outgoing_edges = 2
def run(self, **kwargs):
if '?' in kwargs['query']:
return (kwargs, 'output_1')
else:
return (kwargs, 'output_2')
pipe = Pipeline()
pipe.add_node(component=QueryClassifier(), name='QueryClassifier', inputs=['Query'])
pipe.add_node(component=es_retriever, name='ESRetriever', inputs=['QueryClassifier.output_1'])
pipe.add_node(component=dpr_retriever, name='DPRRetriever', inputs=['QueryClassifier.output_2'])
pipe.add_node(component=JoinDocuments(join_mode='concatenate'), name='JoinResults', inputs=['ESRetriever', 'DPRRetriever'])
pipe.add_node(component=reader, name='QAReader', inputs=['JoinResults'])
res = pipe.run(query='What did Einstein work on?', top_k_retriever=1)
from haystack.pipeline import GenerativeQAPipeline
pipe = GenerativeQAPipeline(generator=rag_generator, retriever=retriever)
res = pipe.run(query='What causes aurora borealis?', top_k_retriever=3)
retrieved_docs = retriever.retrieve(query="who got the first nobel prize in physics?")
predicted_result = generator.predict(
question="who got the first nobel prize in physics?",
documents=retrieved_docs,
top_k=1
)
dense_passage_retriever.train(
data_dir="/data/dpr",
train_filename="train.json",
dev_filename="dev.json",
batch_size=16,
embed_title=True,
num_hard_negatives=1,
n_epochs=3
)
document_store = FAISSDocumentStore(sql_url="sqlite:///mydb.db", vector_size=768)
tika_converter = TikaConverter(
tika_url="http://localhost:9998/tika",
remove_numeric_tables=False,
remove_whitespace=False,
remove_empty_lines=False,
remove_header_footer=False,
valid_languages=None,
)
result = tika_converter.convert(file_path="documents/report.docx")
print(result["text"])
reader.train(
data_dir="data/squad",
train_filename="train-v2.0.json",
num_processes=8,
)
from haystack.retriever.dense import DensePassageRetriever
retriever = DensePassageRetriever(
document_store=document_store,
embedding_model="dpr-bert-base-nq",
do_lower_case=True,
use_gpu=True
)
results = retriever.retrieve(query="What is cosine similarity?")
document_store.add_eval_data("../data/nq/nq_dev_subset_v2.json")
retriever.eval(top_k=10)
reader.eval(document_store=document_store, device=device)
finder.eval(top_k_retriever=10, top_k_reader=10)
from haystack.indexing.file_converters.pdf import PDFToTextConverter
converter = PDFToTextConverter(
remove_header_footer=True,
remove_numeric_tables=True,
valid_languages=["de", "en"]
)
pages = converter.extract_pages(file_path="report.pdf") Summary
Haystack is an open-source AI agent framework that allows for the construction of production-ready Retrieval Augmented Generation (RAG) pipelines and autonomous agents. It is free to use under its open-source license. Developers can integrate it as a library into their own applications, and the documentation describes its functionality in relation to other vector store frameworks. It is aimed at developers building LLM applications, and its materials indicate active development.
Open-source AI orchestration framework for building context-engineered, production-ready LLM applications. Design modular pipelines and agent workflows with explicit control over retrieval, routing, memory, and generation. Built for scalable agents, RAG, multimodal applications, semantic search, and conversational systems.
What Haystack answers
What types of components can I connect in a pipeline?
it constructs Retrieval Augmented Generation (RAG) pipelines and autonomous agents
How do I use it within my existing application code?
developers can integrate it as a library into their own applications
What does it connect with regarding document storage?
its documentation describes its functionality in relation to other vector store frameworks
Where does its functionality stop?
it is designed for building RAG pipelines and autonomous agents
What programming model does it support?
it is an open-source AI agent framework
Does it require a central deployment?
it is free to use under its open-source license
Examples
Command line
No option matches that search.
| option | found in | since | description |
|---|
No option matches that search.
Values are placeholders taken from each option’s declared default. Nothing is executed here — the output shown is a recording of a run that already happened.
Release history
- v3.1.0
Haystack v3.1.0 adds context compaction hooks, token counters, AgentTool for multi-agent delegation, and a new
HAYSTACK_UNSAFE_DESERIALIZATIONenv var.└──▷ GET THIS VERSION$ git clone --branch v3.1.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v3.1.0
└──▷ USE ITPrevent context-window overflows in a long-running agent by sliding off old turns automatically.from haystack.hooks.compaction import CompactionHook, SlidingWindowCompactor from haystack.components.agents import Agent from haystack.components.generators.chat import OpenAIResponsesChatGenerator hook = CompactionHook( compactor=SlidingWindowCompactor(), context_window=128_000, compact_at=0.8, compact_to=0.5, ) agent = Agent( chat_generator=OpenAIResponsesChatGenerator(model="gpt-4o"), tools=[web_search], hooks={"before_llm": [hook]}, ) result = agent.run([ChatMessage.from_user("Summarize recent AI news.")])Estimate token usage before sending a request to decide whether compaction is needed, without any extra dependencies.from haystack.token_counters import ApproximateTokenCounter, TiktokenCounter from haystack.dataclasses import ChatMessage messages = [ChatMessage.from_user("Explain quantum entanglement.")] # No extra install required approx_count = ApproximateTokenCounter(chars_per_token=4.0).count(messages) # Closer estimate for OpenAI models — requires: pip install tiktoken tiktoken_count = TiktokenCounter(encoding="o200k_base").count(messages) print(f"Approximate: {approx_count}, Tiktoken: {tiktoken_count}")Build a multi-agent system where a coordinator delegates web research to a specialist agent.from haystack.components.agents import Agent from haystack.components.generators.chat import OpenAIResponsesChatGenerator from haystack.dataclasses import ChatMessage from haystack.tools import AgentTool researcher = Agent( chat_generator=OpenAIResponsesChatGenerator(model="gpt-4o-mini"), system_prompt="You are a research specialist. Investigate the task and report your findings.", tools=[web_search], ) research_specialist = AgentTool( agent=researcher, name="research", description="Research a question on the web and report the findings", ) coordinator = Agent( chat_generator=OpenAIResponsesChatGenerator(model="gpt-4o"), tools=[research_specialist], system_prompt="You coordinate specialists. Delegate research questions, then answer the user.", ) result = coordinator.run([ChatMessage.from_user("What are the latest LLM benchmarks?")]) print(result["last_message"].text)- ›Adds
CompactionHook(fromhaystack.hooks.compaction) withcontext_window,compact_at, andcompact_toparameters, wired into Agent viahooks={'before_llm': [hook]}, to automatically shorten conversation history before LLM calls. - ›Adds
SlidingWindowCompactor(fromhaystack.hooks.compaction) that drops oldest full turns then individual steps, replacing removed content with an omission note. - ›Adds
ToolResultPruningCompactor(fromhaystack.hooks.compaction) withmin_keep_stepsandmin_tokensparameters that replaces older/large tool results with placeholders while preserving the most recent tool-calling steps. - ›Adds
haystack.token_countersmodule with three classes:ApproximateTokenCounter(dependency-free, configurable viachars_per_token),TiktokenCounter(local estimation viaencodingparameter, requirespip install tiktoken), andOpenAITokenCounter(calls OpenAI's counting API for exact model-specific counts), all exposing a .count(messages) method. - ›Adds
AgentTool(fromhaystack.tools) to wrap any Agent as a Tool so an orchestrating agent can delegate to it; exposesnameanddescriptionparameters and surfaces only the wrapped agent's final reply to the caller.
+8 moreshow less
- ›Adds Agent.clone() method that returns a new Agent with the same configuration, accepting keyword arguments to override init parameters (e.g., agent.clone(system_prompt='Answer in German.')).
- ›Adds
link_formatparameter toPyPDFToDocumentandPDFMinerToDocumentcomponents, parsing PDF annotation links and appending them to page content (matching existingDOCXToDocumentbehavior). - ›Adds
exit_reasonoutput to Agent.run(), returning'text', the name of the tool that satisfied an exit condition, or'max_agent_steps'; also accessible in hooks via state.get('exit_reason'). - ›Adds close() and close_async() resource-release methods to
AutoMergingRetriever,CacheChecker,DocumentWriter,FilterRetriever, andSentenceWindowRetriever. - ›Adds
HAYSTACK_UNSAFE_DESERIALIZATIONenvironment variable (truthy values:1ortrue) to bypass all deserialization safety checks process-wide forPipeline.load,Pipeline.loads,Pipeline.from_dict,Tool.from_dict,State.from_dict, and theConditionalRouter/OutputAdapterJinja sandbox; value is read once and frozen for the process lifetime. - ›Adds
agent.resolved_state_schemapublic attribute exposing the full effective runtime schema including internal keys (messages,step_count,token_usage,exit_reason). - ›Adds
inputs_formatfield toPipelineSnapshot.pipeline_stateto distinguish the new per-sender input shape{component: {socket: [{sender: ..., value: ...}]}}from the legacy flattened shape. - ›Adds a content-free
haystack.agent.hooktracing span for every Agent hook invocation, recording hook point, hook name, hook type, compaction strategy, estimated context size, compaction trigger status, token target, and whether the compactor returned a replacement.
└──▷ BREAKING ON UPGRADE- !
OutputAdapterandConditionalRoutercomponents serialized withunsafe: truenow raiseDeserializationErroron load unless Pipeline.load(..., unsafe=True) (orPipeline.loads/Pipeline.from_dictwithunsafe=True) is used. - !
exit_reasonis now a reserved key inAgent.state_schema; defining a customstate_schemakey namedexit_reasonraisesValueErrorat Agent initialization. - !
Agent.state_schemanow contains only the user-provided schema, excluding internally managed keys (messages,step_count,token_usage,exit_reason); useagent.resolved_state_schemato get the full effective schema. - !
PipelineSnapshot.pipeline_state.inputsandBreakpointException.inputschanged shape from{component: {socket: value}}to{component: {socket: [{sender: ..., value: ...}]}}; readinginputs['my_component']['my_socket']must becomeinputs['my_component']['my_socket'][0]['value']. - !
DocumentMAPEvaluatorscores may change: average precision now uses all unique valid ground-truth values as the denominator and credits each value at most once; existing evaluation baselines must be recalculated. - !Passing
window_size=0toSentenceWindowRetriever.runorSentenceWindowRetriever.run_asyncnow raisesValueErrorinstead of silently falling back to the constructor value; pass None or omit the argument to use the constructor default. - !
InMemoryDocumentStore.get_metadata_field_unique_valuesand its async counterpart now matchsearch_termagainst the metadata field value (case-insensitive substring) instead of the document content; callers relying on content-matching must filter documents themselves. - !The Agent now calls warm_up() on hooks before every run (not only the first); hooks with expensive setup in warm_up() must guard against repeated calls (e.g.,
if self._client is not None: return). - !The internal
_is_warmed_upflag that prevented repeated warm_up() calls on Toolset is removed; every call now reaches warm_up() directly, so custom Tool or Toolset subclasses with expensive setup in warm_up() must add their own guard.
- ›Adds
- v3.1.0-rc3
Haystack v3.1.0-rc3 adds context compaction for Agent, token counters, OpenAI token counting API, PDF link extraction, and a process-wide unsafe deserialization env var.
└──▷ GET THIS VERSION$ git clone --branch v3.1.0-rc3 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v3.1.0-rc3
└──▷ USE ITCount tokens in a message list before sending to an OpenAI model, to decide whether compaction is needed.from haystack.dataclasses import ChatMessage from haystack.token_counters import OpenAITokenCounter counter = OpenAITokenCounter("gpt-5-mini") count = counter.count([ChatMessage.from_user("Summarize the quarterly report.")]) print(count)Attach context compaction to an Agent so long conversations are automatically trimmed before each LLM call.from haystack.components.agents import Agent from haystack.components.generators.chat import OpenAIResponsesChatGenerator from haystack.hooks.compaction import CompactionHook, SlidingWindowCompactor hook = CompactionHook( compactor=SlidingWindowCompactor(), context_window=400_000, compact_at=0.7, compact_to=0.4, ) agent = Agent( chat_generator=OpenAIResponsesChatGenerator(model="gpt-5.4-nano"), tools=[web_search], hooks={"before_llm": [hook]}, )Enable process-wide unsafe deserialization in a trusted deployment so every pipeline load skips safety checks without passingunsafe=Trueat each call site.$ export HAYSTACK_UNSAFE_DESERIALIZATION=1- ›Adds
HAYSTACK_UNSAFE_DESERIALIZATIONenvironment variable (truthy values:1ortrue) as a process-wide switch to skip all deserialization safety checks acrossPipeline.load,Pipeline.loads,Pipeline.from_dict,Tool.from_dict,State.from_dict,ConditionalRouter, andOutputAdapter— read once on first deserialization and frozen for the process lifetime. - ›Adds
haystack.token_countersmodule with aTokenCounterprotocol and two implementations:ApproximateTokenCounter(no extra deps, estimates fromchars_per_token) andTiktokenCounter(requirespip install tiktoken, uses the named encoding such aso200k_base) for sizingChatMessagelists before a call. - ›Adds experimental
CompactionHookandSlidingWindowCompactorinhaystack.hooks.compaction, configurable viacontext_window,compact_at, andcompact_tofractions, wired into an Agent throughhooks={'before_llm': [hook]}; implements the Compactor protocol for custom strategies. - ›Adds
exit_reasonoutput to Agent, returning'text','max_agent_steps', or the name of the exit-condition tool; also accessible in hooks via state.get('exit_reason'). - ›Adds
link_formatparameter toPyPDFToDocumentandPDFMinerToDocument, parsing links from PDF annotations and appending them at the bottom of page content.
+1 moreshow less
- ›Adds
agent.resolved_state_schemapublic attribute for inspecting the full runtime state schema (including internally managed keys such asmessages,step_count,token_usage,exit_reason).
└──▷ BREAKING ON UPGRADE- !
exit_reasonis now a reserved key onAgent.state_schema; initializing an Agent with a customstate_schemacontainingexit_reasonraisesValueError. - !
Agent.state_schemanow contains only the user-provided schema as passed to__init__; code that readagent.state_schemato inspect the full runtime schema must switch toagent.resolved_state_schema. - !
PipelineSnapshot.pipeline_state.inputs(andBreakpointException.inputs) changed shape from{component: {socket: value}}to{component: {socket: [{"sender": ..., "value": ...}]}}; code reading these fields directly must index with[0]["value"]. A newinputs_formatfield records which shape a snapshot uses. - !Loading a serialized
OutputAdapterorConditionalRouterwithunsafe: truein its init parameters now raisesDeserializationErrorunless the pipeline is loaded with Pipeline.load(..., unsafe=True) (or the equivalentPipeline.loads/Pipeline.from_dictoption). - !
SentenceWindowRetriever.runandSentenceWindowRetriever.run_asyncnow raiseValueErrorwhenwindow_size=0is passed at runtime; callers relying on0to mean 'use the constructor value' must omit the argument or pass None instead. - !
InMemoryDocumentStore.get_metadata_field_unique_values(and its async counterpart)search_termparameter now matches against the metadata field value (case-insensitive substring) instead of document content. - !
Toolset._is_warmed_upinternal flag is removed; warm_up() is now called before every run rather than only the first, so custom Tool or Toolset implementations that do expensive setup there must add their own early-return guard. - !
DocumentMAPEvaluatorscores may change because average precision now uses all unique, valid ground-truth comparison values as its denominator and credits each value at most once; existing evaluation baselines should be recalculated. - !Serialized
OutputAdapterandConditionalRoutercomponents containing Jinjacustom_filtersmust now be loaded with Pipeline.load(..., unsafe=True) (orPipeline.loads/Pipeline.from_dictwithunsafe=True).
- ›Adds
- v3.1.0-rc2
Haystack v3.1.0-rc2 adds context compaction for Agents, token counters, OpenAI token counting API, PDF link extraction, and a process-wide unsafe deserialization env var.
└──▷ GET THIS VERSION$ git clone --branch v3.1.0-rc2 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v3.1.0-rc2
└──▷ USE ITAttach context compaction to an Agent so long conversations are automatically trimmed before hitting the model's context limit.from haystack.components.agents import Agent from haystack.components.generators.chat import OpenAIResponsesChatGenerator from haystack.hooks.compaction import CompactionHook, SlidingWindowCompactor hook = CompactionHook( compactor=SlidingWindowCompactor(), context_window=400_000, compact_at=0.7, compact_to=0.4, ) agent = Agent( chat_generator=OpenAIResponsesChatGenerator(model="gpt-5.4-nano"), tools=[web_search], hooks={"before_llm": [hook]}, )Count tokens for a list of ChatMessages using OpenAI's exact token counting API before deciding whether to compact.from haystack.dataclasses import ChatMessage from haystack.token_counters import OpenAITokenCounter counter = OpenAITokenCounter("gpt-5-mini") count = counter.count([ChatMessage.from_user("Hello!")]) print(count)Enable unsafe deserialization process-wide when deploying with fully trusted pipelines and you cannot passunsafe=Trueat every call site.$ HAYSTACK_UNSAFE_DESERIALIZATION=1 python my_pipeline_server.py- ›Adds
HAYSTACK_UNSAFE_DESERIALIZATIONenvironment variable (truthy values:1ortrue) as a process-wide switch to skip all deserialization safety checks acrossPipeline.load,Pipeline.loads,Pipeline.from_dict,Tool.from_dict,State.from_dict,ConditionalRouter, andOutputAdapterJinja sandbox flags — intended for deployments loading only fully trusted pipelines. - ›Adds
haystack.token_countersmodule with aTokenCounterprotocol and two implementations:ApproximateTokenCounter(no dependencies, estimates from text length viachars_per_tokenparameter) andTiktokenCounter(closer estimates for OpenAI models, requirespip install tiktoken, accepts anencodingparameter such as'o200k_base'). - ›Adds
link_formatparameter toPyPDFToDocumentandPDFMinerToDocumentcomponents, parsing links from PDF annotations and appending them at the bottom of each page.
└──▷ BREAKING ON UPGRADE- !
exit_reasonis now a reserved state key on Agent; if yourstate_schemadefines a key namedexit_reason, the Agent raisesValueErrorat initialization — rename the key. - !
Agent.state_schemanow contains only the user-provided schema as passed to__init__; code that readagent.state_schemato inspect the full runtime schema must switch toagent.resolved_state_schema. - !
PipelineSnapshot.pipeline_state.inputs(andBreakpointException.inputs) changed shape from{component: {socket: value}}to{component: {socket: [{"sender": ..., "value": ...}]}}; code that reads these fields directly must be updated;inputs_formatfield records which shape a snapshot uses. - !Loading a serialized
OutputAdapterorConditionalRouterwithunsafe: truein its init parameters now raisesDeserializationErrorunlessPipeline.load,Pipeline.loads, orPipeline.from_dictis called withunsafe=True. - !Serialized
OutputAdapterandConditionalRoutercomponents containing Jinjacustom_filtersmust now be loaded with Pipeline.load(..., unsafe=True) (or equivalentPipeline.loads/Pipeline.from_dictoption). - !Passing
window_size=0toSentenceWindowRetriever.runorSentenceWindowRetriever.run_asyncnow raisesValueErrorinstead of silently falling back to the constructor value; pass None or omit the argument to use the constructor'swindow_size. - !
InMemoryDocumentStore.get_metadata_field_unique_valuessearch_termparameter now matches against the metadata field value (case-insensitive substring) instead of document content; callers relying on content-matching must pre-filter documents themselves. - !The Toolset internal
_is_warmed_upflag is removed; warm_up() is now called before every Agent run, so custom Tool or Toolset implementations doing expensive setup must guard with their own state (e.g.if self._client is not None: return). - !
DocumentMAPEvaluatorscores may change because average precision now uses all unique, valid ground-truth comparison values as its denominator and credits each value at most once — re-baseline any evaluations that depended on previous scores.
- ›Adds
- v3.1.0-rc1
Haystack v3.1.0-rc1 adds Agent context compaction, AgentTool, exit_reason output, OpenAITokenCounter, and PDF link extraction.
└──▷ GET THIS VERSION$ git clone --branch v3.1.0-rc1 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v3.1.0-rc1
└──▷ USE ITAutomatically compact an Agent's conversation when it fills 70% of the model's context window, keeping the most recent turns.from haystack.components.agents import Agent from haystack.components.generators.chat import OpenAIResponsesChatGenerator from haystack.hooks.compaction import CompactionHook, SlidingWindowCompactor hook = CompactionHook( compactor=SlidingWindowCompactor(), context_window=400_000, compact_at=0.7, compact_to=0.4, ) agent = Agent( chat_generator=OpenAIResponsesChatGenerator(model="gpt-4o"), tools=[web_search], hooks={"before_llm": [hook]}, )Count tokens in a list of ChatMessages before sending them to an OpenAI model, including tool schemas, to check context headroom.from haystack.dataclasses import ChatMessage from haystack.token_counters import TiktokenCounter counter = TiktokenCounter(encoding="o200k_base") messages = [ChatMessage.from_user("Summarize the last 10 CVEs.")] token_count = counter.count(messages, tools=[my_tool]) print(token_count)Route Agent output downstream based on why it stopped — text reply, tool exit, or step budget exhausted.from haystack.components.agents import Agent from haystack.components.routers import ConditionalRouter # Agent now includes exit_reason in its output result = agent.run(messages=[ChatMessage.from_user("Research CVE-2024-1234.")]) print(result["exit_reason"]) # 'text', 'max_agent_steps', or a tool name- ›Adds
CompactionHookandSlidingWindowCompactor(inhaystack.hooks.compaction) to automatically shorten Agent conversation history before LLM calls, configured viacontext_window,compact_at, andcompact_toparameters. - ›Adds experimental
ToolResultPruningCompactor(inhaystack.hooks.compaction) that reduces Agent context by replacing older large tool results with short placeholders, controlled bymin_keep_stepsandmin_tokensparameters. - ›Adds
OpenAITokenCounterinhaystack.token_countersthat uses OpenAI's token-counting API to return model-specific counts forChatMessageobjects and optional tool schemas. - ›Adds
haystack.token_countersmodule with aTokenCounterprotocol and two implementations:ApproximateTokenCounter(configurablechars_per_token, no dependencies) andTiktokenCounter(uses OpenAI's byte-pair encoder, requirespip install tiktoken); both accept an optionaltoolsargument to account for tool schema tokens. - ›Adds Agent.clone() method to create a new Agent with the same configuration, optionally overriding init parameters (e.g. agent.clone(system_prompt='Answer in German.')).
+5 moreshow less
- ›Adds
AgentTool, a Tool that wraps a Haystack Agent so it can be delegated to by another Agent, enabling multi-agent systems. - ›Adds
exit_reasonoutput to Agent runs — one of'text', the name of the tool that satisfied an exit condition, or'max_agent_steps'— also accessible to hooks via state.get('exit_reason'). - ›Adds
agent.resolved_state_schemapublic attribute exposing the full effective runtime schema, including internally managed keys. - ›Adds
link_formatparameter to bothPyPDFToDocumentandPDFMinerToDocumentcomponents to parse and append links from PDF annotations to page content, matching existingDOCXToDocumentfunctionality. - ›Adds
inputs_formatfield toPipelineState, recording whether a snapshot uses the legacy flattened shape or the new per-sender list shape{component: {socket: [{sender: ..., value: ...}]}}.
└──▷ BREAKING ON UPGRADE- !
exit_reasonis now a reserved key in Agent state schema; initializing an Agent with a customstate_schemakey namedexit_reasonraisesValueError. - !
Agent.state_schemanow contains only the user-provided schema (as passed to__init__), not the resolved runtime schema; use the newagent.resolved_state_schemato inspect the full effective schema. - !
DocumentMAPEvaluatoraverage precision scores have changed: the denominator is now all unique valid ground-truth comparison values and each value is credited at most once; re-baseline evaluations that relied on previous scores. - !
PipelineSnapshot.pipeline_state.inputs(andBreakpointException.inputs) changed shape from{component: {socket: value}}to{component: {socket: [{sender: ..., value: ...}]}}; read values asinputs['my_component']['my_socket'][0]['value']. - !Loading a serialized
OutputAdapterorConditionalRouterwithunsafe=Truenow raisesDeserializationErrorunless the pipeline is loaded with Pipeline.load(..., unsafe=True) (orPipeline.loads/Pipeline.from_dictwithunsafe=True). - !Passing
window_size=0toSentenceWindowRetriever.runorSentenceWindowRetriever.run_asyncnow raisesValueError; omit the argument or pass None to use the constructor's value. - !
InMemoryDocumentStore.get_metadata_field_unique_valuessearch_termparameter now matches against the metadata field's own value (case-insensitive substring) instead of the document's content. - !The
Toolset._is_warmed_upinternal flag is removed; warm_up() is now called before every Agent run on Tools, Toolsets, and hooks — guard expensive setup with your own state (e.g.if self._client is not None: return).
- ›Adds
- v3.0.0
Haystack 3.0 ships a hooks-driven Agent, unified async Pipeline, built-in introspection, safe deserialization, and mock test components.
└──▷ GET THIS VERSION$ git clone --branch v3.0.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v3.0.0
└──▷ USE ITAudit every tool call before execution — useful for compliance logging or human approval gates.from haystack.components.agents import Agent from haystack.components.generators.chat import OpenAIChatGenerator from haystack.hooks import hook @hook def audit_tool_calls(state): pending = state.data['messages'][-1].tool_calls print(f'about to run: {[tc.tool_name for tc in pending]}') agent = Agent( chat_generator=OpenAIChatGenerator(), tools=[...], hooks={'before_tool': [audit_tool_calls]}, ) result = agent.run(messages=[{'role': 'user', 'content': 'Summarize recent alerts'}])Load a serialized pipeline from an untrusted source with a scoped allowlist to prevent arbitrary code execution.from haystack import Pipeline with open('pipeline.yaml') as fp: pipeline = Pipeline.load(fp, allowed_modules=['mypkg.*'])- ›Adds a hooks system to Agent with lifecycle points
before_run,before_llm,before_tool,after_tool,on_exit, andafter_run— pass callables decorated with@hookvia thehooksdict argument to enforce guardrails, audit tool calls, or inject human-in-the-loop checkpoints. - ›Adds
ConfirmationHook(human-in-the-loop) andToolResultOffloadHook(writes large tool results to a store, leaving a compact pointer in conversation) as built-inbefore_toolhooks. - ›Adds
SkillToolsetfor first-class skill discovery via progressive disclosure — the model sees only names and one-line descriptions until a skill is loaded, keeping context window usage lean. - ›Adds dynamic tool selection at runtime: pass
tools=...toAgent.run/Agent.run_asyncso one Agent instance can serve different teams, tenants, and tasks without re-initialization. - ›Adds native async tool support —
@toolroutesasync defcallables to a Tool's newasync_functionfield.
+10 moreshow less
- ›Adds built-in Agent state keys
step_count,token_usage, andtool_call_countsfor run introspection — react to them in hooks to compact context, cap tool loops, or apply cost budgets. - ›Emits dedicated step-level tracing spans
haystack.agent.stepwith nested.llmand.toolchildren tagged with tools actually used, enabling precise per-step observability. - ›Unifies Pipeline and
AsyncPipelineinto a single Pipeline class exposingrun,run_async,run_async_generator, andstreammethods — stream() yieldsStreamingChunks as produced and exposes final output onhandle.result. - ›Adds symmetric
warm_up/closelifecycle to Pipeline and components so long-running services can acquire and release connections, GPU memory, and file handles without leaks. - ›Adds pipeline deserialization allowlist via Pipeline.load(fp, allowed_modules=[...]), the
HAYSTACK_DESERIALIZATION_ALLOWLISTenvironment variable, and allow_deserialization_module(...) — dangerous builtins (eval,exec,open,getattr) are blocked by default; trusted sources can passunsafe=True. - ›Adds
MockChatGenerator,MockTextEmbedder, andMockDocumentEmbeddertest components — no API keys or network required; embedders return stable, hash-derived embeddings for deterministic CI. - ›Adds
{% insert %}Jinja2 tag to Agent,PromptBuilder, andChatPromptBuilderfor interleaving runtime messages into templates. - ›Moves 30 components (Sentence Transformers, Hugging Face local/API, Whisper, spaCy/langdetect, Tika, Azure OCR, SerperDev/SearchApi, OpenAPI connectors, Datadog/OpenTelemetry tracers) to independently released packages in
haystack-core-integrations, enabling releases independent of the core cycle. - ›All Chat Generators now accept a plain
strformessages, easing migration from removed text-only generators. - ›Tracing is now explicit — add
OpenTelemetryConnectororDatadogConnectoror call tracing.enable_tracing(...) to activate; Haystack no longer auto-enables tracing or reconfiguresstructlogprocess-wide.
└──▷ BREAKING ON UPGRADE- !
AsyncPipelineis removed; replace all imports and instantiations with Pipeline. Note thatPipeline.runexecutes components sequentially and does not acceptconcurrency_limit; use await pipeline.run_async(...) in async contexts. - !Async pipeline tracing now uses the operation name
haystack.pipeline.run(with taghaystack.pipeline.execution_mode=async) instead of the formerhaystack.async_pipeline.run. - !
ToolInvoker(standalone) is removed; tool execution is now owned entirely by Agent. - !
OpenAIGenerator,AzureOpenAIGenerator,HuggingFaceAPIGenerator, andHuggingFaceLocalGeneratorare removed — use their Chat Generator counterparts (OpenAIChatGenerator, etc.). - !
DALLEImageGeneratoris renamed toOpenAIImageGenerator. - !Agent,
PromptBuilder, andChatPromptBuildernow treat every Jinja2 template variable as required by default (required_variables='*'); passrequired_variables=Noneto restore the previous all-optional behavior. - !Tools must declare
inputs_from_stateexplicitly to read a State value; implicit injection by parameter name no longer works. - !
continue_runis now a reserved key inAgent.state_schema; passing it raisesValueError— rename conflicting keys (e.g. tomy_continue_run). - !
step_count,token_usage, andtool_call_countsare now reserved keys inAgent.state_schema; passing any of them raisesValueError— rename conflicting keys. - !
Document.idis now computed from canonical, key-sorted JSON ofmeta, so documents with non-emptymetaget different IDs than in 2.x. - !
configure_loggingnow attaches only to Haystack's own loggers; importing Haystack no longer reconfiguresstructlogprocess-wide. - !Tracing is no longer auto-enabled; explicitly add an
OpenTelemetryConnectororDatadogConnectoror call tracing.enable_tracing(...) to activate. - !Components that use external resources now create them during
warm_uprather than__init__; errors from missing API keys or other init-time checks now surface atwarm_uptime instead. - !Passing
toolsat runtime via run(tools=...) to a chat generator that does not support tools now raisesTypeErrorinstead of silently ignoring them. - !The 30 components moved to
haystack-core-integrationsrequire a new package install and import path change (e.g.pip install sentence-transformers-haystackandfrom haystack_integrations.components.embedders.sentence_transformers import SentenceTransformersTextEmbedder). - !
haystack-experimentalis no longer a core dependency. - !Confirmation hook strategies now receive model-requested tool arguments in
tool_paramsrather than fully-prepared arguments (values injected from State are no longer included).
- ›Adds a hooks system to Agent with lifecycle points
- v2.31.0
Haystack v2.31.0 adds async evaluators, type-preserving routing, YAML frontmatter extraction, and expanded reference range support.
└──▷ GET THIS VERSION$ git clone --branch v2.31.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.31.0
└──▷ USE ITRoute a structured dataclass through a pipeline without losing its type — useful when downstream components expect a typed object, not a string.from haystack.components.routers import ConditionalRouter routes = [ { "condition": "{{query.intent == 'search'}}", "output": "query", "output_name": "search_query", "output_type": ParsedQuery, "output_passthrough": True, }, ] router = ConditionalRouter(routes) result = router.run(query=ParsedQuery(text="What is Haystack?", intent="search", entities=[])) assert result["search_query"].intent == "search" # type preservedRun multiple RAG evaluations concurrently in a FastAPI service without blocking the event loop.from haystack.components.evaluators import FaithfulnessEvaluator import asyncio evaluator = FaithfulnessEvaluator() result = await evaluator.run_async( questions=["What is Haystack?"], contexts=[["Haystack is an AI framework."]], predicted_answers=["Haystack is an AI framework."] )- ›Adds
output_passthrough: Truefield toConditionalRouterroute definitions, bypassing Jinja2 rendering so complex types like dataclasses and Pydantic models are passed through unchanged rather than silently stringified. - ›Adds
extract_frontmatter=Trueparameter toMarkdownToDocument; when set, YAML frontmatter is stripped from converted content and stored inDocument.meta. - ›Adds
expand_reference_rangesparameter toAnswerBuilder; when enabled, citation ranges like[6-10]and[1-3,7-9]are expanded to individual document indices in RAG answers (disabled by default). - ›Adds
document_comparison_fieldparameter toDocumentNDCGEvaluator, allowing document matching by'content','id', or any'meta.<key>'field when calculating NDCG scores, consistent withDocumentMAPEvaluator,DocumentMRREvaluator, andDocumentRecallEvaluator. - ›Adds native async support via
run_asyncto LLMEvaluator,FaithfulnessEvaluator, andContextRelevanceEvaluator, enabling concurrent evaluation in async applications like FastAPI or FastMCP without blocking the event loop.
└──▷ BREAKING ON UPGRADE- !
DocumentNDCGEvaluatornow matches documents bycontentinstead ofidby default; existing pipelines may see changed NDCG scores. Passdocument_comparison_field="id"to restore the previous behavior.
- ›Adds
- v2.30.1
AzureOpenAIChatGenerator now accepts Secret for
azure_endpointandapi_version, enabling runtime env-var resolution.└──▷ GET THIS VERSION$ git clone --branch v2.30.1 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.30.1
└──▷ USE ITUse a single serialized pipeline across dev and prod Azure OpenAI deployments by resolving the endpoint and API version from environment variables at runtime.from haystack.components.generators.chat import AzureOpenAIChatGenerator from haystack.utils import Secret generator = AzureOpenAIChatGenerator( azure_deployment="gpt-4o", azure_endpoint=Secret.from_env_var("AZURE_OPENAI_ENDPOINT"), api_version=Secret.from_env_var("AZURE_OPENAI_API_VERSION"), )- ›Adds Secret type support to the
azure_endpointandapi_versionparameters ofAzureOpenAIChatGenerator, allowing values to be resolved at runtime via Secret.from_env_var() so a single serialized pipeline can target different environments by swapping environment variables.
- ›Adds Secret type support to the
- v2.30.0
Haystack v2.30.0 adds syntax-aware Python code splitting and plain-string input for all ChatGenerators.
└──▷ GET THIS VERSION$ git clone --branch v2.30.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.30.0
└──▷ USE ITSplit a Python source file for a code-RAG pipeline while keeping class definitions attached to their method chunks and docstrings in metadata.from haystack.components.preprocessors import PythonCodeSplitter splitter = PythonCodeSplitter( max_effective_lines=80, strip_docstrings=True, preserve_class_definition=True, ) result = splitter.run(documents=[doc]) for chunk in result["documents"]: print(chunk.meta["start_line"], chunk.meta["unit_kinds"], chunk.content[:120])Quickly probe an OpenAI chat model with a one-liner string instead of constructing a ChatMessage list.from haystack.components.generators.chat import OpenAIChatGenerator generator = OpenAIChatGenerator() response = generator.run("Summarize the OWASP Top 10 in three sentences.") print(response["replies"][0].text)- ›Introduces
PythonCodeSplittercomponent (importable fromhaystack.components.preprocessors) that parses Python source files via theastmodule and merges units — module docstrings, import blocks, top-level functions, class headers, methods, nested classes — into chunks of roughlymax_effective_lineslines, keeping whole functions and methods intact. - ›Adds
strip_docstrings=Trueparameter toPythonCodeSplitterto move docstrings into chunk metadata instead of inline content. - ›Adds
preserve_class_definition=Trueparameter toPythonCodeSplitterto prepend the enclosing class signature to chunks whose members spill into a later chunk. - ›Adds
oversized_factorparameter toPythonCodeSplitterto control the threshold at which an oversized function falls back to a line-based secondary split (delegating toDocumentSplitter) with overlap. - ›Each
PythonCodeSplitterchunk carries metadata fieldsstart_line,end_line,unit_kinds,include_classes,decorators,docstrings,source_id, andsplit_idfor rich downstream filtering.
+4 moreshow less
- ›All
ChatGeneratorcomponents now accept a plainstrfor themessagesparameter, automatically wrapping it in aChatMessagewith theuserrole — applies toAzureOpenAIChatGenerator,AzureOpenAIResponsesChatGenerator,FallbackChatGenerator,HuggingFaceAPIChatGenerator,HuggingFaceLocalChatGenerator,OpenAIChatGenerator, andOpenAIResponsesChatGenerator. - ›Adds
run_asynctoTextEmbeddingRetriever,MultiQueryEmbeddingRetriever, andMultiQueryTextRetriever, enabling native coroutine execution inAsyncPipelinewith fallback to a thread executor. - ›Updates
ToolsTypeso that any class inheriting from Tool or Toolset is accepted in any sequence type (list, tuple, etc.) for thetoolsparameter. - ›Pipeline.draw() and Pipeline.show() now validate the Mermaid server response against expected output formats (PNG, JPEG, WebP, SVG, PDF) via magic-byte signature and Content-Type header before writing to disk, raising
PipelineDrawingErroron mismatch.
└──▷ BREAKING ON UPGRADE- !
DALLEImageGeneratordefaultmodelchanged fromdall-e-3togpt-image-2; acceptedqualityvalues changed fromstandard/hdtoauto/high/medium/low; acceptedsizevalues changed to1024x1024,1024x1536,1536x1024, orauto; theresponse_formatparameter is now ignored and the component always returns base64-encoded JSON.
- ›Introduces
- v2.29.0
Haystack v2.29.0 adds MultiRetriever and TextEmbeddingRetriever for hybrid search, plus async CacheChecker support.
└──▷ GET THIS VERSION$ git clone --branch v2.29.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.29.0
└──▷ USE ITBuild a hybrid BM25 + embedding search pipeline that lets you skip the embedding retriever for short keyword queries at runtime.from haystack.components.retrievers import MultiRetriever, TextEmbeddingRetriever from haystack.components.retrievers.in_memory import InMemoryBM25Retriever, InMemoryEmbeddingRetriever from haystack.components.embedders import SentenceTransformersTextEmbedder retriever = MultiRetriever( retrievers={ "bm25": InMemoryBM25Retriever(document_store=doc_store), "embedding": TextEmbeddingRetriever( retriever=InMemoryEmbeddingRetriever(document_store=doc_store), text_embedder=SentenceTransformersTextEmbedder(model="sentence-transformers/all-MiniLM-L6-v2"), ), }, top_k=3, ) # Full hybrid search result = retriever.run(query="green energy sources") # BM25 only for short/keyword queries result = retriever.run(query="solar", active_retrievers=["bm25"])Switch MultiRetriever from reciprocal rank fusion to simple concatenation when you want raw ranked lists joined in order rather than RRF-scored.retriever = MultiRetriever( retrievers={ "bm25": InMemoryBM25Retriever(document_store=doc_store), "embedding": TextEmbeddingRetriever( retriever=InMemoryEmbeddingRetriever(document_store=doc_store), text_embedder=SentenceTransformersTextEmbedder(model="sentence-transformers/all-MiniLM-L6-v2"), ), }, join_mode="concatenate", top_k=5, )- ›Adds
MultiRetrievercomponent (importable fromhaystack.components.retrievers) that runs multiple text retrievers in parallel, merges results via reciprocal rank fusion by default, and acceptsactive_retrieversandtop_kparameters at runtime to selectively enable/disable individual retrievers. - ›Adds
join_modeparameter toMultiRetriever, supporting'reciprocal_rank_fusion'(default) and'concatenate'merge strategies. - ›Adds
TextEmbeddingRetrievercomponent (importable fromhaystack.components.retrievers) that wraps an embedding retriever with a text embedder into a singleTextRetriever-protocol-compatiblecomponent, enabling use insideMultiRetriever. - ›Adds
run_asyncmethod toCacheChecker, enabling non-blocking use inAsyncPipeline. - ›Adds two usage modes to the
LLMcomponent: template-variable mode (provideuser_promptwith Jinja2 variables such as{{ query }}to expose them as pipeline inputs) and pass-through mode (omituser_promptto makemessagesa required input accepting a fully-constructedChatMessagelist).
+1 moreshow less
- ›Extracts reciprocal rank fusion logic into shared utility
_reciprocal_rank_fusioninhaystack.utils.misc, now used by bothMultiRetrieverandDocumentJoiner.
└──▷ BREAKING ON UPGRADE- !
LLM.runandLLM.run_asyncno longer acceptmessagesandstreaming_callbackas positional arguments — they must now be passed as keyword arguments (e.g. llm.run(messages=[message], streaming_callback=my_callback)).
- ›Adds
- v2.28.0
Haystack v2.28.0 lets tools and components receive the live agent State object directly, and adds async support to LLMMetadataExtractor and a header-depth filter to MarkdownHeaderSplitter.
└──▷ GET THIS VERSION$ git clone --branch v2.28.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.28.0
└──▷ USE ITSplit a Markdown document only on top-level and second-level headers, keeping deeper headers merged into their parent chunk.from haystack.components.preprocessors import MarkdownHeaderSplitter splitter = MarkdownHeaderSplitter(header_split_levels=[1, 2], keep_headers=True) result = splitter.run(documents=[document])["documents"]
Give a function-based tool read/write access to the full agent state without manually wiring individual keys.from haystack.components.agents import State from haystack.tools import tool @tool def my_tool(query: str, state: State) -> str: """Search using context from agent state.""" history = state.get("history") ...- ›Adds
header_split_levelsparameter (list of integers 1–6, default all levels) toMarkdownHeaderSplitterto control which header depths create split boundaries — e.g.,header_split_levels=[1, 2]splits only on#and##headers. - ›Adds
run_asyncmethod toLLMMetadataExtractor;ChatGeneratorrequests now run concurrently using the existingmax_workersinit parameter. - ›Enables tools and components to declare a State (or
State | None) parameter in their signature to receive the live agent State object at invocation time — no extra wiring needed;ToolInvokerautomatically injects it and excludes it from the LLM-facing schema. - ›
MarkdownHeaderSplitternow ignores#lines inside fenced code blocks (triple-backtick or triple-tilde), preventing hash-prefixed lines in code from being misidentified as Markdown headers.
└──▷ BREAKING ON UPGRADE- !
request_with_retryandasync_request_with_retryinhaystack.utils.requests_utilsnow raisehttpx.HTTPErrorinstead ofrequests.exceptions.RequestExceptionon failure; code catchingrequests.exceptions.RequestException(including viaHuggingFaceTEIRanker) must be updated to catchhttpx.HTTPError. - !The
LLMcomponent now requiresuser_promptto be provided at initialization and it must contain at least one Jinja2 template variable;required_variablesnow defaults to'*'and passing an empty list raises aValueError. - !Agent.run() and Agent.run_async() now require
messagesas an explicit argument; code relying on the default None value from v2.26/v2.27 must pass an empty list instead: agent.run(messages=[], ...).
- ›Adds
- v2.27.0
Haystack v2.27.0 adds automatic list joining in pipelines, async document store helpers, and multimodal chat generator support.
└──▷ GET THIS VERSION$ git clone --branch v2.27.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.27.0
└──▷ USE ITInspect metadata value ranges in a local prototype store before moving to production — replaces manual document iteration.from haystack.document_stores.in_memory import InMemoryDocumentStore store = InMemoryDocumentStore() # ... write documents ... print(store.get_metadata_field_min_max("year")) print(store.get_metadata_field_unique_values("category")) print(store.count_documents_by_filter({"field": "meta.category", "operator": "==", "value": "finance"}))Check which Azure OpenAI models Haystack recognises without consulting external docs.from haystack.components.generators.chat import AzureOpenAIChatGenerator print(AzureOpenAIChatGenerator.SUPPORTED_MODELS)
- ›Adds
count_documents_by_filter,count_unique_metadata_by_filter,get_metadata_fields_info,get_metadata_field_min_max, andget_metadata_field_unique_valuesoperations toInMemoryDocumentStore, matching the inspection and filtering API available in other document stores. - ›Adds async variants to
InMemoryDocumentStore: update_by_filter_async(), count_documents_by_filter_async(), count_unique_metadata_by_filter_async(), get_metadata_fields_info_async(), get_metadata_field_min_max_async(), and get_metadata_field_unique_values_async(). - ›Exposes
SUPPORTED_MODELSclass variable onAzureOpenAIChatGenerator, listing supported model IDs such asgpt-5-miniandgpt-4o, inspectable at runtime viaAzureOpenAIChatGenerator.SUPPORTED_MODELS. - ›Adds partial support for the
image-text-to-texttask inHuggingFaceLocalChatGenerator, enabling use of multimodal models such as Qwen 3.5 or Ministral with text-only inputs. - ›Pipelines now automatically join multiple inputs into a list-typed input socket with type conversion, supporting
T + T -> list[T],T + list[T] -> list[T],str + ChatMessage -> list[str], andstr + ChatMessage -> list[ChatMessage]— eliminating the need for extra joining components.
+1 moreshow less
- ›Adds
_to_trace_dictmethod toImageContentandFileContentdataclasses, replacing largebase64_imageandbase64_datafields with placeholder strings (e.g.'Base64 string (N characters)') when tracing is enabled.
- ›Adds
- v2.26.0
Haystack v2.26.0 adds LLMRanker, Jinja2 agent system prompts,
SUPPORTED_MODELSclass variables, and async embedding splitting.└──▷ GET THIS VERSION$ git clone --branch v2.26.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.26.0
└──▷ USE ITRerank retrieved documents semantically using an LLM rather than a cross-encoder, filtering out irrelevant results before stuffing context into a RAG prompt.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"Dynamically adapt agent behavior at runtime (e.g. response language) without redefining the prompt for each context.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", ) print(result["last_message"].text)Tune the LLM-facing search tool metadata in a large toolset so a specific model finds tools more reliably.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'.", }, )- ›Adds LLMRanker component in
haystack.components.rankersthat reranks documents using aChatGeneratorandPromptBuilderwith JSON-formatted LLM output; supports configurable prompts, optional custom chat generators, runtimetop_koverrides, and serialization. - ›Agent
system_promptparameter now accepts Jinja2 message template syntax (e.g.{% message role='system' %}...{% endmessage %}), with runtime variables passed atruntime alongside arequired_variablesinit parameter for validation. - ›
OpenAIChatGenerator,OpenAIResponsesChatGenerator, andAzureOpenAIResponsesChatGeneratornow expose aSUPPORTED_MODELSclass variable listing supported model IDs (e.g.gpt-4o,gpt-5-mini). - ›
SearchableToolsetadds three new optional__init__parameters —search_tool_name,search_tool_description, andsearch_tool_parameters_description— to customize the bootstrap search tool's LLM-facing metadata. - ›Adds
run_asyncmethod toEmbeddingBasedDocumentSplitterenabling async embedding-based document splitting.
+5 moreshow less
- ›
HuggingFaceAPIDocumentEmbedder.run_asyncgains aconcurrency_limitparameter to control concurrent embedding inference requests, improving async throughput. - ›Components whose input types are a union of lists (e.g.
list[str] | list[ChatMessage]) now support multiple input connections in pipelines, extending beyond the previous bare-list and optional-list limitation. - ›The
messagesruntime parameter toAgent.runis now optional, allowing the agent to execute with only auser_prompt. - ›Pipeline and
AsyncPipelinenow log a warning identifying misconfigured components when a component returns output keys not declared in its@component.output_types, replacing a previously confusing 'Pipeline Blocked' error. - ›Adds Python 3.14 support to Haystack.
- ›Adds LLMRanker component in
- v2.25.1
Haystack v2.25.1 extends auto variadic sockets to support
Optional[list[...]]input types.└──▷ GET THIS VERSION$ git clone --branch v2.25.1 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.25.1
- ›Auto variadic sockets now support
Optional[list[...]]input types in addition to plainlist[...], enabling nullable list inputs to participate in variadic connection fan-in.
- ›Auto variadic sockets now support
- v2.25.0
Haystack v2.25.0 adds SearchableToolset for BM25 tool discovery, a simplified LLM component, and Jinja2-templated Agent prompts.
└──▷ GET THIS VERSION$ git clone --branch v2.25.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.25.0
└──▷ USE ITLet an agent search a large tool catalog at runtime instead of loading every tool into context upfront.from haystack.components.agents import Agent from haystack.components.generators.chat import OpenAIChatGenerator from haystack.dataclasses import ChatMessage from haystack.tools import Tool, SearchableToolset catalog = [ Tool(name="get_weather", description="Get weather for a city"), Tool(name="search_web", description="Search the web"), # ... hundreds more tools ] toolset = SearchableToolset(catalog=catalog) agent = Agent(chat_generator=OpenAIChatGenerator(), tools=toolset) result = agent.run(messages=[ChatMessage.from_user("What's the weather in Milan?")])Reuse a templated Agent prompt across multiple invocations — useful for translation or summarization pipelines where only the input variable changes.from haystack.components.agents import Agent from haystack.components.generators.chat import OpenAIChatGenerator agent = Agent( chat_generator=OpenAIChatGenerator(), system_prompt="You are a helpful translation assistant.", user_prompt="""{% message role="user"%} Translate the following document to {{ language }}: {{ document }} {% endmessage %}""", required_variables=["language", "document"], ) result = agent.run(language="French", document="The weather is lovely today.")Use the new LLM component for single-turn, tool-free generation with a templated prompt — ideal for document summarization steps inside a pipeline.from haystack.components.generators.chat import LLM, OpenAIChatGenerator llm = LLM( chat_generator=OpenAIChatGenerator(), system_prompt="You are a helpful assistant.", user_prompt="""{% message role="user"%} Summarize the following document: {{ document }} {% endmessage %}""", required_variables=["document"], ) result = llm.run(document="Haystack v2.25.0 introduces SearchableToolset and a new LLM component.") print(result["last_message"].text)- ›Adds
SearchableToolsettohaystack.tools, enabling agents to dynamically discover tools from large catalogs via BM25 keyword search; starts agents with a singlesearch_toolsfunction and supports configurable search threshold for automatic passthrough mode and top-k result limiting. - ›Adds
user_promptandrequired_variablesparameters to the Agent component, enabling reusable Jinja2-templated user prompts that can be passed dynamic variables at runtime without manually constructingChatMessageobjects. - ›Adds new
LLMcomponent athaystack.components.generators.chat.LLM— a single-turn, tool-free text generation interface supporting system prompts, Jinja2-templateduser_prompt,required_variables, streaming callbacks, and bothrunandrun_asyncexecution. - ›Adds
link_formatparameter toPPTXToDocumentandXLSXToDocumentconverters, supporting hyperlink extraction in'markdown'([text](url)),'plain'(text (url)), or'none'(default, text only) formats. - ›Adds
FileToFileContentcomponent to convert local files intoFileContentobjects that can be embedded intoChatMessagefor LLM input.
+5 moreshow less
- ›Adds
document_comparison_fieldparameter toDocumentMRREvaluator,DocumentMAPEvaluator, andDocumentRecallEvaluator, enabling document comparison by fields other thancontent, includingidand metadata keys viameta.<key>syntax. - ›Adds support for
transformersv5, unlocking faster model loading, improved quantization support, and faster inference for selected models while retaining compatibility with v4. - ›Haystack now emits a Warning when dataclass instances (Document,
ChatMessage,StreamingChunk,ByteStream,SparseEmbedding) are mutated in place, guiding users towarddataclasses.replacefor safe copies. - ›
LLMDocumentContentExtractornow extracts both content and metadata from image-based documents — when the LLM returns JSON,document_contentfills the document body and other keys are merged into metadata; errors are now recorded inextraction_errormetadata instead ofcontent_extraction_error. - ›
EmbeddingBasedDocumentSplitterandMultiQueryEmbeddingRetrievernow automatically invoke warm_up() when run() is called if not yet warmed up.
└──▷ BREAKING ON UPGRADE- !The
PipelineTemplateandPredefinedPipelineclasses and the Pipeline.from_template() method have been removed; migrate to YAML-based pipeline definitions. - !
HuggingFaceLocalGeneratordefaulttaskchanged fromtext2text-generationtotext-generationand default model changed fromgoogle/flan-t5-basetoQwen/Qwen3-0.6B; existing configs explicitly settingtask='text2text-generation'must be updated totask='text-generation'or pintransformers<5.
- ›Adds
- v2.24.0
Haystack v2.24.0 eliminates adapter boilerplate with native type coercion, adds FileContent for PDF inputs, and introduces MarkdownHeaderSplitter.
└──▷ GET THIS VERSION$ git clone --branch v2.24.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.24.0
└──▷ USE ITAttach a PDF to a chat message and send it to an OpenAI model for summarization — no file-parsing pipeline required.from haystack.components.generators.chat.openai import OpenAIChatGenerator from haystack.dataclasses.chat_message import ChatMessage from haystack.dataclasses.file_content import FileContent file_content = FileContent.from_url("https://arxiv.org/pdf/2309.08632") chat_message = ChatMessage.from_user(content_parts=[file_content, "Summarize this paper in 100 words."]) llm = OpenAIChatGenerator(model="gpt-4.1-mini") response = llm.run(messages=[chat_message])Wire two file-type converters directly to aDocumentWriterwithout aDocumentJoinerin an ingestion pipeline.from haystack import Pipeline from haystack.components.converters import HTMLToDocument, TextFileToDocument from haystack.components.routers import FileTypeRouter from haystack.components.writers import DocumentWriter from haystack.dataclasses import ByteStream from haystack.document_stores.in_memory import InMemoryDocumentStore doc_store = InMemoryDocumentStore() pipe = Pipeline() pipe.add_component("router", FileTypeRouter(mime_types=["text/plain", "text/html"])) pipe.add_component("txt_converter", TextFileToDocument()) pipe.add_component("html_converter", HTMLToDocument()) pipe.add_component("writer", DocumentWriter(doc_store)) pipe.connect("router.text/plain", "txt_converter.sources") pipe.connect("router.text/html", "html_converter.sources") pipe.connect("txt_converter.documents", "writer.documents") pipe.connect("html_converter.documents", "writer.documents")Build a query-rewriting RAG pipeline where the LLM'slist[ChatMessage]output is automatically coerced tostrfor the BM25 retriever — noOutputAdapterneeded.from haystack import Pipeline from haystack.components.builders import ChatPromptBuilder from haystack.components.generators.chat import OpenAIChatGenerator from haystack.components.retrievers import InMemoryBM25Retriever from haystack.document_stores.in_memory import InMemoryDocumentStore p = Pipeline() p.add_component("prompt_builder", ChatPromptBuilder(template=template)) p.add_component("llm", OpenAIChatGenerator(model="gpt-4.1-mini")) p.add_component("retriever", InMemoryBM25Retriever(document_store=document_store, top_k=3)) # list[ChatMessage] from llm is auto-converted to str for retriever p.connect("prompt_builder", "llm") p.connect("llm", "retriever")- ›Introduces the
FileContentdataclass (importable fromhaystack.dataclasses.file_content) enablingChatMessageobjects to carry file inputs (e.g. PDFs via FileContent.from_url(...)) forOpenAIChatGeneratorandAzureOpenAIChatGenerator, withOpenAIResponsesChatGeneratorandAzureOpenAIResponsesChatGeneratoralso supported. - ›Introduces the
MarkdownHeaderSplittercomponent that splits documents at Markdown headers (#,##, etc.), preserves header hierarchy as metadata, supports secondary splitting modes (word, passage, period, or line) via Haystack'sDocumentSplitter, and handles edge cases such as no headers or empty content. - ›Adds delete_all_documents(), update_by_filter(), and delete_by_filter() operations to
InMemoryDocumentStore, with corresponding standard DocumentStore tests for all three. - ›Adds
run_asyncmethod toSearchApiWebSearchandSerperDevWebSearchcomponents. - ›Pipelines now natively connect multiple
list[T]outputs to a singlelist[T]input without aListJoinerorDocumentJoiner, enabling direct multi-converter-to-writer wiring via pipe.connect().
+4 moreshow less
- ›Pipelines automatically convert between
ChatMessageandstrtypes on connection:str→ userChatMessage, andChatMessage→str(via.text); raisesPipelineRuntimeErrorif.textis None. - ›Pipelines support list wrapping (
T→list[T]) and list collapsing (list[T]→Tusing first element, forstrandChatMessageonly); raisesPipelineRuntimeErroron empty list. - ›Agent components now accept a tuple of tool names as a key in
confirmation_strategies, allowing multiple tools to share a singleBlockingConfirmationStrategyinstead of requiring one entry per tool. - ›All Rankers (
HuggingFaceTEIRanker,LostInTheMiddleRanker,MetaFieldRanker,MetaFieldGroupingRanker,SentenceTransformersDiversityRanker,SentenceTransformersSimilarityRanker,TransformersSimilarityRanker) now deduplicate documents byidbefore ranking, removing the need for aDocumentJoinerafter hybrid retrieval.
└──▷ BREAKING ON UPGRADE- !All Rankers (
HuggingFaceTEIRanker,LostInTheMiddleRanker,MetaFieldRanker,MetaFieldGroupingRanker,SentenceTransformersDiversityRanker,SentenceTransformersSimilarityRanker,TransformersSimilarityRanker) now deduplicate documents byidbefore ranking; pipelines that relied on duplicate documents with the same user-definedidpassing through the ranker will silently drop those duplicates. - !
MultiQueryEmbeddingRetrieverandMultiQueryTextRetrievernow deduplicate byidinstead of by document content; setups where multiple documents share identical content but differentidvalues will no longer be deduplicated, and setups expecting content-based deduplication will behave differently. - !The deprecated
deserialize_document_store_in_init_params_inplacefunction (deprecated in Haystack 2.23.0) has been removed.
- ›Introduces the
- v2.23.0
Haystack v2.23.0 adds human-in-the-loop agent confirmation strategies, image-returning tools, and automatic custom-component serialization.
└──▷ GET THIS VERSION$ git clone --branch v2.23.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.23.0
└──▷ USE ITReturn an image from a tool and let an agent describe it using a multimodal provider.from haystack.components.agents import Agent from haystack.components.generators.chat import OpenAIResponsesChatGenerator from haystack.tools import ComponentTool from haystack.dataclasses import ChatMessage, ImageContent from haystack import component @component class ImageRetriever: @component.output_types(images=list[ImageContent]) def run(self): return {"images": [ImageContent.from_file_path("/data/image.jpg")]} image_tool = ComponentTool( component=ImageRetriever(), outputs_to_string={"raw_result": True, "source": "images"} ) agent = Agent( chat_generator=OpenAIResponsesChatGenerator(model="gpt-5-nano"), system_prompt="Retrieve images and describe them.", tools=[image_tool], ) result = agent.run(messages=[ChatMessage.from_user("Retrieve the image and describe it.")]) print(result["last_message"].text)Persist a pipeline snapshot to a database instead of disk by supplying a custom callback to Pipeline.run().import json def save_to_db(snapshot: dict) -> None: db.snapshots.insert_one({"data": json.dumps(snapshot)}) result = pipeline.run( data={"query": "What is RAG?"}, snapshot_callback=save_to_db )- ›Adds
confirmation_strategiesparameter to Agent, accepting per-toolBlockingConfirmationStrategyinstances driven byAlwaysAskPolicy,AskOncePolicy, orNeverAskPolicy, with pluggable UIs (RichConsoleUI,SimpleConsoleUI) — enabling agents to pause for human approval before executing tools. - ›Expands
ToolCallResult.resultto accept lists ofTextContentandImageContentobjects, allowing tools to return images to providers such asOpenAIResponsesChatGeneratorandAnthropicChatGenerator. - ›Adds
raw_resultkey support to theoutputs_to_stringparameter of Tool,ComponentTool, andPipelineToolfor returning image results without string conversion. - ›Adds
outputs_to_stringparameter tocreate_tool_from_functionand the@tooldecorator for additional customization of tool output formatting. - ›Adds
snapshot_callbackparameter to Pipeline.run() to handle pipeline snapshots with a custom function (e.g., saving to a database or remote service) instead of the default file-saving behavior.
+4 moreshow less
- ›Adds
HAYSTACK_PIPELINE_SNAPSHOT_SAVE_ENABLEDenvironment variable to explicitly enable saving pipeline snapshots to disk (disabled by default); customsnapshot_callbackfunctions are invoked regardless of this setting. - ›component_from_dict() and component_to_dict() now automatically handle serialization of custom components containing
DocumentStore, Secret,ComponentDevice, or any object with to_dict()/from_dict() — no manual override needed. - ›
OpenAIResponsesChatGeneratornow supports flattenedgeneration_kwargskeysreasoning_effort,reasoning_summary, andverbositydirectly, without nesting them in sub-objects. - ›Adds
haystack.component.fully_qualified_typefield to component tracing output, providing the full module path and class name (e.g.,haystack.components.generators.chat.openai.OpenAIChatGenerator) alongside the existinghaystack.component.typefield.
└──▷ BREAKING ON UPGRADE- !Pipeline snapshot file saving is now disabled by default; set
HAYSTACK_PIPELINE_SNAPSHOT_SAVE_ENABLED=trueto restore the previous behavior. - !Pipeline snapshots created before Haystack 2.22.0 that contain
pipeline_outputswithout theserialization_schemaandserialized_datastructure are no longer supported — recreate snapshots with the current version before upgrading. - !The
return_empty_on_no_matchparameter has been fully removed fromRegexTextExtractor; passing it during component initialization now raises an error (it is silently ignored during pipeline deserialization).
- ›Adds
- v2.22.0
Haystack v2.22.0 adds semantic document splitting, auto warm-up, multi-output tools, and Qwen3 reranker support.
└──▷ GET THIS VERSION$ git clone --branch v2.22.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.22.0
└──▷ USE ITSplit a long document into semantically coherent chunks instead of fixed-size windows, so downstream retrievers see topically consistent passages.from haystack.components.embedders import SentenceTransformersDocumentEmbedder from haystack.components.preprocessors import EmbeddingBasedDocumentSplitter embedder = SentenceTransformersDocumentEmbedder() splitter = EmbeddingBasedDocumentSplitter( document_embedder=embedder, sentences_per_group=2, percentile=0.95, min_length=50, max_length=1000 ) result = splitter.run(documents=[doc])Give an LLM agent formatted search results and a count summary from a single tool call, hiding raw debug data from the model.from haystack.tools import Tool tool = Tool( name="search", description="Search for documents", parameters={...}, function=search_func, outputs_to_string={ "formatted_docs": {"source": "documents", "handler": format_documents}, "summary": {"source": "metadata", "handler": format_summary} # 'debug_info' is omitted and will not be stringified } )Rerank retrieved passages with the Qwen3 reranker model, which requires custom prefix/suffix tokens around query and document text.from haystack.components.rankers.sentence_transformers_similarity import SentenceTransformersSimilarityRanker ranker = SentenceTransformersSimilarityRanker( model="tomaarsen/Qwen3-Reranker-0.6B-seq-cls", query_prefix='<|im_start|>system\nJudge whether the Document meets the requirements...\n<Query>: ', query_suffix="\n", document_prefix="<Document>: ", document_suffix="<|im_end|>\n<|im_start|>assistant\n<think>\n\n</think>\n\n", ) result = ranker.run(query="Which planet is known as the Red Planet?", documents=[...])- ›Adds
EmbeddingBasedDocumentSplittertohaystack.components.preprocessors, splitting documents by semantic similarity using a pluggable embedder; constructor acceptsdocument_embedder,sentences_per_group,percentile,min_length, andmax_lengthparameters. - ›Adds
outputs_to_stringconfiguration to Tool, letting a single tool expose multiple named string outputs (each with asourceandhandler) so the LLM receives rich, selectively stringified context without additional tool calls. - ›Adds
query_suffixanddocument_suffixparameters toSentenceTransformersSimilarityRanker, enabling compatibility with the Qwen3 reranker model family (e.g.,tomaarsen/Qwen3-Reranker-0.6B-seq-cls). - ›Adds
enable_thinkingparameter to chat generators for thinking-capable models, allowing intermediate chain-of-thought reasoning steps before final responses. - ›Adds reasoning content support to
HuggingFaceAPIChatGenerator, extracting chain-of-thought output (e.g., from DeepSeek R1) in both streaming and non-streaming modes; accessible viareply.reasoning.reasoning_text.
+4 moreshow less
- ›Components with a
warm_upmethod now execute it automatically on first use, eliminating the need to call warm_up() manually before standalone usage. - ›Adds construction-time validation of
inputs_from_stateandoutputs_to_stateparameters in the Tool class, catching invalid state-mapping configuration early via function introspection and JSON schema checks. - ›Adds support for PEP 604 union type syntax (
X | Y,X | None) in component type annotations alongside the existingUnion[X, Y]/Optional[X]forms. - ›Agent tracing spans are now nested under the component span when an Agent runs inside a Pipeline, enabling proper hierarchical trace visualization in Datadog, Braintrust, and OpenTelemetry backends.
└──▷ BREAKING ON UPGRADE- !Python 3.9 is no longer supported; Haystack now requires Python 3.10 or later.
- !
HuggingFaceLocalChatGeneratornow defaults toQwen/Qwen3-0.6B, replacing the previous default model — existing pipelines that relied on the old default will silently switch models on upgrade.
- ›Adds
- v2.21.0
Haystack v2.21.0 adds Multi-Query RAG components and async support for FilterRetriever and AutoMergingRetriever.
└──▷ GET THIS VERSION$ git clone --branch v2.21.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.21.0
└──▷ USE ITExpand an ambiguous or short query into multiple variations and retrieve a broader set of relevant documents using BM25.from haystack.components.query import QueryExpander from haystack.components.retrievers import InMemoryBM25Retriever, MultiQueryTextRetriever from haystack.document_stores.in_memory import InMemoryDocumentStore from haystack.components.writers import DocumentWriter from haystack import Document from haystack.document_stores.types import DuplicatePolicy store = InMemoryDocumentStore() writer = DocumentWriter(document_store=store, policy=DuplicatePolicy.SKIP) writer.run(documents=[Document(content="Renewable energy comes from wind and sunlight.")]) expander = QueryExpander() retriever = InMemoryBM25Retriever(document_store=store, top_k=3) multi_retriever = MultiQueryTextRetriever(retriever=retriever) expanded = expander.run(query="renewable energy") results = multi_retriever.run(queries=expanded["queries"]) for doc in results["documents"]: print(doc.content)- ›Adds
QueryExpandercomponent (importable fromhaystack.components.query) to generate semantically similar query variations for broader search coverage. - ›Adds
MultiQueryTextRetriever(importable fromhaystack.components.retrievers) to run multiple queries in parallel against a text-based retriever (e.g., BM25) and merge results by score. - ›Adds
MultiQueryEmbeddingRetriever(importable fromhaystack.components.retrievers) to perform multi-query retrieval using embeddings for richer semantic recall. - ›Adds
return_empty_on_no_matchparameter to RegexTextExtractor.__init__() (default True); set to False to return{'captured_text': ''}instead of{}when no regex match is found, ensuring consistent output structure for pipeline integration. - ›
FilterRetrieverandAutoMergingRetrievercomponents now support asynchronous execution.
└──▷ BREAKING ON UPGRADE- !The default model for
AzureOpenAIGeneratorandAzureOpenAIChatGeneratorchanged fromgpt-4o-minitogpt-4.1-mini, and the default API version changed from2023-05-15to2024-12-01-preview. - !The default model for
OpenAIChatGeneratorandOpenAIGeneratorchanged fromgpt-4o-minitogpt-5-mini; explicitly passmodel='gpt-4o-mini'at initialization to retain the previous behavior.
- ›Adds
- v2.20.0
Haystack v2.20.0 adds OpenAI Responses API components, async retriever support, and richer AnswerBuilder controls.
└──▷ GET THIS VERSION$ git clone --branch v2.20.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.20.0
└──▷ USE ITUse OpenAI's Responses API with a reasoning model and built-in web search tool to get answers with low reasoning effort.from haystack.components.generators.chat import OpenAIResponsesChatGenerator from haystack.dataclasses import ChatMessage chat_generator = OpenAIResponsesChatGenerator( model="o3-mini", generation_kwargs={"summary": "auto", "effort": "low"}, tools=[{"type": "web_search"}], ) response = chat_generator.run(messages=[ChatMessage.from_user("What's a positive news story from today?")]) print(response["replies"][0].text)- ›Adds
OpenAIResponsesChatGeneratorcomponent integrating OpenAI's Responses API, supporting reasoning summaries viageneration_kwargs(e.g.summary,effort), native OpenAI/MCP tool formats, and Haystack Tool/Toolset objects. - ›Adds
AzureOpenAIResponsesChatGeneratorcomponent bringing the same Responses API capabilities to Azure OpenAI deployments, configured viaazure_endpointandazure_deployment. - ›Returns logprobs in
ChatMessage.metaforOpenAIChatGeneratorandOpenAIResponsesChatGeneratorwhen logprobs are enabled ingeneration_kwargs. - ›Adds
extrafield toToolCallandToolCallDeltadataclasses to store provider-specific information. - ›Adds run_async() method to
SentenceWindowRetrieverfor use in async pipelines and workflows.
+8 moreshow less
- ›Adds warm_up() method to
OpenAIChatGenerator,AzureOpenAIChatGenerator,HuggingFaceAPIChatGenerator,HuggingFaceLocalChatGenerator, andFallbackChatGeneratorto initialize tools before pipeline execution without requiring an Agent component. - ›Adds
return_only_referenced_documentsparameter (default: True) toAnswerBuilder, plussource_index(1-based) andreferenced(boolean) fields in returned documentmetadictionaries. - ›Adds
generation_kwargsparameter to the Agent component for run-time control over chat generation. - ›Adds
revisionparameter toSentenceTransformersDocumentEmbedder,SentenceTransformersTextEmbedder,SentenceTransformersSparseDocumentEmbedder, andSentenceTransformersSparseTextEmbedderfor pinning a specific model version from the Hugging Face Hub. - ›Updates
PipelineSnapshotsserialization and deserialization to work with pydanticBaseModels. - ›Updates Agent,
LLMMetadataExtractor,LLMMessagesRouter, andLLMDocumentContentExtractorto automatically call self.warm_up() at runtime if not already warmed up, removing the need for a manual pre-call. - ›Improves log-trace correlation for
DatadogTracerusing ddtrace.tracer.get_log_correlation_context(). - ›Redesigns Toolset.warm_up() so the base method warms all tools by default, with subclasses able to override for custom initialization; simplifies warm_up_tools() to delegate to Toolset.warm_up().
- ›Adds
- v2.19.0
Haystack v2.19.0 adds FallbackChatGenerator, sparse embedders, RegexTextExtractor, and mixed Tool/Toolset support for agents.
└──▷ GET THIS VERSION$ git clone --branch v2.19.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.19.0
└──▷ USE ITBuild a resilient chat pipeline that automatically falls back through Anthropic, Google, and OpenAI when earlier providers fail.from haystack.components.generators.chat.fallback import FallbackChatGenerator from haystack.components.generators.chat.openai import OpenAIChatGenerator from haystack_integrations.components.generators.anthropic import AnthropicChatGenerator from haystack.dataclasses import ChatMessage chat_generator = FallbackChatGenerator(chat_generators=[ AnthropicChatGenerator(model="claude-sonnet-4-5", timeout=5), OpenAIChatGenerator(model="gpt-4o-mini"), ]) response = chat_generator.run(messages=[ChatMessage.from_user("Summarize the OWASP Top 10.")]) print(response["meta"]["successful_chat_generator_class"]) print(response["replies"][0].text)Embed documents as sparse vectors for efficient inverted-index retrieval with QdrantDocumentStore.from haystack.components.embedders import SentenceTransformersSparseTextEmbedder embedder = SentenceTransformersSparseTextEmbedder() embedder.warm_up() result = embedder.run("Detect lateral movement via SMB.") print(result["sparse_embedding"]) # SparseEmbedding(indices=[...], values=[...])Mix standalone tools and toolsets in a single Agent, and override the tool subset at runtime for a specific invocation.from haystack.components.agents import Agent from haystack.tools import Tool, Toolset agent = Agent( chat_generator=generator, tools=[math_toolset, weather_toolset, calendar_tool], ) # At runtime, restrict to only the tools needed for this task response = agent.run( messages=[ChatMessage.from_user("What is 42 * 7?")], tools=["multiply"], )- ›Adds
FallbackChatGeneratorinhaystack.components.generators.chat.fallbackthat tries a list of chat generators sequentially and returns the first successful response, withmeta['successful_chat_generator_class']identifying which provider succeeded — handles timeouts, rate limits, and server errors transparently. - ›Adds
conversion_mode='row'parameter toCSVToDocument, with optionalcontent_column; each CSV row becomes a separate Document with remaining columns stored inmeta(default'file'mode preserved). - ›Adds
pipeline_snapshotandpipeline_snapshot_file_pathparameters toBreakpointException, andpipeline_snapshot_file_pathtoPipelineRuntimeError, for easier location and inspection of stored pipeline snapshots. - ›Introduces
SentenceTransformersSparseTextEmbedderandSentenceTransformersSparseDocumentEmbeddercomponents inhaystack.components.embeddersfor sparse embedding models compatible with Sentence Transformers; outputSparseEmbeddingobjects are compatible withQdrantDocumentStore. - ›Adds warm_up() method to the Tool dataclass and Toolset, automatically called by Agent and
ToolInvokerduring their warmup phase to support pre-execution initialization such as database connections or model loading.
+6 moreshow less
- ›Adds a new
RegexTextExtractorcomponent that extracts text from chat messages or string inputs based on a custom regex pattern. - ›Adds
toolsas a runtime parameter to Agent.run(), allowing callers to supply a subset of tool names or an entirely new set of Tool objects or a Toolset per invocation. - ›Extends the
toolsparameter on Agent,ToolInvoker,OpenAIChatGenerator,AzureOpenAIChatGenerator,HuggingFaceAPIChatGenerator, andHuggingFaceLocalChatGeneratorto accept a mixed list of Tool and Toolset objects in the same list. - ›Enables resuming an Agent from an
AgentSnapshotwhile simultaneously specifying a new breakpoint in the same run call, supporting stepwise debugging with precise control over chat generator and tool inputs. - ›Updates
PipelineSnapshotserialization and deserialization to support Python Enum classes. - ›Adds
raise_on_failureoption to_save_pipeline_snapshotto control whether save failures raise an exception or are only logged.
└──▷ BREAKING ON UPGRADE- !Requires
openai>=1.99.2due to use ofChatCompletionMessageCustomToolCall; installations with older OpenAI client versions will break.
- ›Adds
- v2.18.1
Haystack v2.18.1 lets agents accept a runtime
toolsparameter to swap or subset tools per invocation.└──▷ GET THIS VERSION$ git clone --branch v2.18.1 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.18.1
- ›Adds
toolsto agentrunparameters, allowing callers to pass a list of tool names (subset selection) or Tool objects / a Toolset (full replacement) at runtime.
- ›Adds
- v2.18.0
Haystack v2.18.0 adds pipeline error snapshots with resume support, PipelineTool, structured outputs for OpenAI generators, and runtime Agent system prompts.
└──▷ GET THIS VERSION$ git clone --branch v2.18.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.18.0
└──▷ USE ITRecover a failed pipeline run and resume from the last successful checkpoint instead of restarting from scratch.try: pipeline.run(data=input_data) except PipelineRuntimeError as exc_info: snapshot = exc_info.value.pipeline_snapshot intermediate_outputs = snapshot.pipeline_state.pipeline_outputs # inspect outputs, fix the issue, then resume pipeline.run(data={}, snapshot=snapshot)Wrap a retrieval pipeline as an LLM-callable tool for use inside an Agent multi-step reasoning workflow.from haystack import Pipeline from haystack.tools import PipelineTool retrieval_pipeline = Pipeline() # ... add components ... retrieval_tool = PipelineTool( pipeline=retrieval_pipeline, input_mapping={"query": ["bm25_retriever.query"]}, output_mapping={"ranker.documents": "documents"}, name="retrieval_tool", description="Use to retrieve documents", )Extract structured data from unstructured text using a Pydantic model as the response format.from pydantic import BaseModel from haystack.components.generators.chat import OpenAIChatGenerator from haystack.dataclasses import ChatMessage class CalendarEvent(BaseModel): event_name: str event_date: str event_location: str generator = OpenAIChatGenerator( model="gpt-4o-2024-08-06", generation_kwargs={"response_format": CalendarEvent} ) result = generator.run([ChatMessage.from_user("The Open NLP Meetup is in Berlin on September 19.")]) print(result["replies"][0].text)- ›Adds
snapshotargument to pipeline.run() to resume a failed pipeline from its last successful checkpoint, and exposespipeline_snapshot.pipeline_state.pipeline_outputson thePipelineRuntimeErrorexception for mid-run inspection. - ›Adds
PipelineToolclass inhaystack.toolsto expose full Haystack Pipelines as LLM-compatible tools, withinput_mappingandoutput_mappingarguments for fine-grained control over which pipeline inputs and outputs are visible to the LLM. - ›Adds
response_formatsupport (Pydantic model or JSON schema) ingeneration_kwargsforOpenAIChatGeneratorandAzureOpenAIChatGenerator; Pydantic models are supported for non-streaming, JSON schema for streaming responses. - ›Adds
request_headersparameter toLinkContentFetcherfor custom per-request HTTP headers, with precedence order: httpx client defaults → component defaults →request_headers→ rotating User-Agent. - ›Adds
exclude_subdomainsparameter toSerperDevWebSearch; when True, restricts results to exact domains inallowed_domains, filtering out subdomains (defaults to False for backward compatibility).
+3 moreshow less
- ›Adds
reasoningfield toStreamingChunkaccepting an optionalReasoningContentdataclass for structured reasoning content in streaming responses. - ›Adds
system_promptto Agent run parameters, enabling dynamic runtime override of the agent's system prompt. - ›Adds HTTP/2 graceful fallback in
LinkContentFetcher: if theh2package is not installed, falls back to HTTP/1.1 with a warning instead of raising an error.
- ›Adds
- v2.17.0
Haystack v2.17.0 adds image support for 12 model providers, ReasoningContent in ChatMessage, and ByteStream routing in MetadataRouter.
└──▷ GET THIS VERSION$ git clone --branch v2.17.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.17.0
└──▷ USE ITStore model reasoning output in an assistant message for providers that expose chain-of-thought or reasoning traces.from haystack.dataclasses import ChatMessage msg = ChatMessage.from_assistant( text="The answer is 42.", reasoning="First I considered the problem domain, then narrowed down..." ) print(msg.reasoning)Override the active tool set at runtime in a previously built pipeline without rebuilding it.tool_invoker.run( messages=chat_history, tools=[search_tool, calculator_tool] # overrides constructor tools )- ›Adds
ReasoningContentas a new content part toChatMessage, storable via thereasoningparameter in ChatMessage.from_assistant(), enabling assistant messages to carry model reasoning text and metadata. - ›Extends
SentenceWindowRetriever'ssource_id_meta_fieldparameter to accept a list of strings, so only documents matching all specified meta fields are retrieved. - ›Adds
raise_on_failureparameter toFileTypeRouter(default False); when set to True, always raisesFileNotFoundErrorfor non-existent files. - ›Extends ToolInvoker.run() to accept a
toolslist argument that overrides the tools set at construction time, enabling runtime tool switching in pre-built pipelines. - ›Adds support for the
|union type operator (Python 3.10+) inserialize_typeand Pipeline.connect(), alongside existingtyping.Unionsupport.
+5 moreshow less
- ›Expands multimodal image support to Amazon Bedrock, Anthropic, Azure, Google, Hugging Face API, Meta Llama API, Mistral, Nvidia, Ollama, OpenAI, OpenRouter, and STACKIT providers.
- ›Adds multimodal support to
HuggingFaceAPIChatGeneratorfor vision-language model usage, allowing both text and images to be sent via Hugging Face APIs. - ›Extends
MetadataRouterto routelist[ByteStream]objects in addition tolist[Documents]. - ›Adds serialization/deserialization methods for
TextContentandImageContentparts ofChatMessage. - ›Supports subclasses of
ChatMessagein Agent state schema validation, checking issubclass(args[0], ChatMessage) instead of requiring exact type equality.
└──▷ BREAKING ON UPGRADE- !
MultiFileConverternow outputs a newfailedkey in its result dictionary containing files that failed to convert; thedocumentsoutput is only included when at least one file is successfully converted (previouslydocumentscould be present but empty). - !
HuggingFaceAPIChatGeneratornow applies the updatedfinish_reasonmapping consistently regardless of streaming mode:eos_token→stop,stop_sequence→stop, tool calls present →tool_calls. Previously this mapping was only applied when streaming was enabled.
- ›Adds
- v2.16.0
Haystack v2.16.0 adds Agent Breakpoints, multimodal image pipelines, HuggingFace TEI reranking, and parallel tool invocation.
└──▷ GET THIS VERSION$ git clone --branch v2.16.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.16.0
└──▷ USE ITPause an Agent mid-run to inspect its internal state during development — useful when debugging complex multi-step reasoning or tool chains.from haystack.dataclasses.breakpoints import AgentBreakpoint, Breakpoint from haystack.dataclasses import ChatMessage chat_generator_breakpoint = Breakpoint( component_name="chat_generator", visit_count=0, snapshot_file_path="debug_snapshots" ) agent_breakpoint = AgentBreakpoint(break_point=chat_generator_breakpoint, agent_name="calculator_agent") response = agent.run( messages=[ChatMessage.from_user("What is 7 * (4 + 2)?")], break_point=agent_breakpoint )Send an image URL to a vision-enabled LLM for description — the starting point for any multimodal RAG or agent pipeline.from haystack.dataclasses import ImageContent, ChatMessage from haystack.components.generators.chat import OpenAIChatGenerator image_content = ImageContent.from_url("https://cdn.britannica.com/79/191679-050-C7114D2B/Adult-capybara.jpg") message = ChatMessage.from_user( content_parts=["Describe the image in short.", image_content] ) llm = OpenAIChatGenerator(model="gpt-4o-mini") print(llm.run([message])["replies"][0].text)Build a multimodal prompt template that compares two images — enables dynamic prompt creation combining text and image inputs in a singleChatPromptBuildercall.from haystack.components.builders import ChatPromptBuilder from haystack.dataclasses.chat_message import ImageContent template = """ {% message role="user" %} Hello! I am {{user_name}}. What's the difference between the following images? {% for image in images %} {{ image | templatize_part }} {% endfor %} {% endmessage %} """ builder = ChatPromptBuilder(template=template) result = builder.run( user_name="John", images=[ ImageContent.from_file_path("apple-fruit.jpg"), ImageContent.from_file_path("apple-logo.jpg") ] )- ›Introduces
AgentBreakpointand Breakpoint classes (importable fromhaystack.dataclasses.breakpoints) to pause, inspect, and resume Agent execution mid-run; pass via thebreak_pointargument to agent.run(). - ›Adds
ImageContentdataclass withbase64_image,mime_type,detail, andmetadatafields, plus convenience class methods ImageContent.from_url() and ImageContent.from_file_path(). - ›Adds image input support to
OpenAIChatGeneratorvia the newImageContentdataclass embedded inChatMessagecontent parts. - ›Adds
PDFToImageContent,ImageFileToImageContent,DocumentToImageContent, andImageFileToDocumentconverter components for building multimodal indexing and retrieval pipelines. - ›Adds
LLMDocumentContentExtractorcomponent to extract text from image-based documents using a vision-enabled LLM.
+19 moreshow less
- ›Adds
SentenceTransformersDocumentImageEmbeddercomponent to generate embeddings from image-based documents using models such as CLIP. - ›Adds
DocumentLengthRoutercomponent to route documents based on textual content length. - ›Adds
DocumentTypeRoutercomponent to route documents automatically based on MIME type metadata. - ›Extends
ChatPromptBuilderto support special string templates (with{% message role='...' %}blocks and thetemplatize_partfilter) enabling dynamic multimodal prompt creation with embedded images. - ›Adds
tool_invoker_kwargsparameter to Agent to pass additional kwargs such asmax_workersandenable_streaming_callback_passthroughthrough toToolInvoker. - ›Adds
enable_streaming_callback_passthroughparameter toToolInvoker.__init__,run, andrun_async; when True, forwardsstreaming_callbackto any tool whoseinvokemethod accepts it. - ›Adds new
HuggingFaceTEIRankercomponent for reranking with the Text Embeddings Inference (TEI) API, supporting both self-hosted TEI services and Hugging Face Inference Endpoints. - ›Adds
raise_on_failureboolean parameter toOpenAIDocumentEmbedderandAzureOpenAIDocumentEmbedder; defaults to False (preserving prior logging behavior); set to True to raise on API errors. - ›Adds
source_id_meta_field,split_id_meta_field, andraise_on_missing_meta_fieldsparameters toSentenceWindowRetrieverfor customizable metadata field names and missing-field handling. - ›
ToolInvokernow executestool_callsin parallel in both sync and async modes. - ›Adds
AsyncHFTokenStreamingHandlerfor async streaming support inHuggingFaceLocalChatGenerator. - ›Adds
tool_calls,tool_call_result,index, andstartfields toStreamingChunkfor richer streaming callback formatting. - ›Adds
ComponentInfodataclass tohaystack.dataclassesand passes it intoStreamingChunkso callers can identify which component originated a stream; supported inOpenAIChatGenerator,AzureOpenAIChatGenerator,HuggingFaceAPIChatGenerator, andHuggingFaceLocalChatGenerator. - ›Adds
to_dictandfrom_dictserialization methods toByteStream,StreamingChunk,ToolCallResult,ToolCall,ComponentInfo, andToolCallDelta. - ›Adds
skip_empty_documentsinit parameter toDocumentSplitter(default True); set to False to retain non-textual documents for downstream components likeLLMDocumentContentExtractor. - ›Adds
return_embeddinginit parameter toInMemoryDocumentStore;bm25_retrievalandfilter_documentsnow honor it to control whether embeddings are returned. - ›Adds
guess_mime_typeparameter to ByteStream.from_file_path(). - ›Makes
PipelineBase.validate_inputa public method, allowing pre-runtime pipeline validation outside of Pipeline.run(). - ›Raises a warning when all remaining pipeline components are blocked and no expected outputs (per Pipeline().outputs()) have been produced, aiding debugging of mutually exclusive branch pipelines.
└──▷ BREAKING ON UPGRADE- !The deprecated
async_executorparameter has been removed fromToolInvoker; usemax_workersinstead. - !The State class has been removed from
haystack.dataclasses; import it fromhaystack.components.agentsinstead. - !The
deserialize_value_with_schema_legacyfunction has been removed frombase_serialization; objects serialized with Haystack 2.14.0 or older using the old State format can no longer be deserialized. - !All parameters of Pipeline.draw() and Pipeline.show() must now be passed as keyword arguments (positional arguments are no longer accepted).
- !
HuggingFaceAPIGeneratormay no longer work with the Hugging Face Inference API; migrate toHuggingFaceAPIChatGeneratorfor generative models via the Hugging Face Inference API.
- ›Introduces
- v2.15.0
Haystack v2.15.0 adds parallel tool calling, LLMMessagesRouter, HuggingFaceTEIRanker, and richer StreamingChunk fields.
└──▷ GET THIS VERSION$ git clone --branch v2.15.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.15.0
└──▷ USE ITRoute user messages through Llama Guard for content moderation before passing safe messages downstream.from haystack.components.generators.chat import HuggingFaceAPIChatGenerator from haystack.components.routers.llm_messages_router import LLMMessagesRouter from haystack.dataclasses import ChatMessage chat_generator = HuggingFaceAPIChatGenerator( api_type="serverless_inference_api", api_params={"model": "meta-llama/Llama-Guard-4-12B", "provider": "groq"}, ) router = LLMMessagesRouter( chat_generator=chat_generator, output_names=["unsafe", "safe"], output_patterns=["unsafe", "safe"], ) print(router.run([ChatMessage.from_user("How to rob a bank?")]))- ›Adds
max_workersparameter toToolInvoker.__init__to configure the internalThreadPoolExecutorused for parallel tool calling, replacing the deprecatedasync_executorparameter. - ›Adds
enable_streaming_callback_passthroughparameter toToolInvoker.init,ToolInvoker.run, andToolInvoker.run_async; when True, passes thestreaming_callbackfunction to a tool's invoke method if the method acceptsstreaming_callbackin its signature. - ›Adds
raise_on_failureboolean parameter toOpenAIDocumentEmbedderandAzureOpenAIDocumentEmbedder; when True, raises an exception on API errors instead of logging and continuing (default is False). - ›Adds
require_tool_call_idsparameter toChatMessage.to_openai_dict_format; set to False to suppress errors when theidfield is missing in a Tool Call, for compatibility with shallow OpenAI-compatible APIs (default is True). - ›Adds
trust_remote_codeparameter toSentenceTransformersSimilarityRanker; when True, enables execution of custom models and scripts hosted on the Hugging Face Hub.
+10 moreshow less
- ›Adds
finish_reasonfield toStreamingChunkusing aFinishReasontype alias with values'stop','length','tool_calls','content_filter', and Haystack-specific'tool_call_results';ToolInvokersetsfinish_reason='tool_call_results'in the final chunk when tool execution completes. - ›Adds
tool_calls,tool_call_result,index, andstartfields toStreamingChunk, plus a newToolCallDeltadataclass forStreamingChunk.tool_callsto represent argument string deltas. - ›Adds new
ComponentInfodataclass passed throughStreamingChunkso streaming callbacks can identify which component produced each chunk; wired intoOpenAIChatGenerator,AzureOpenAIChatGenerator,HuggingFaceAPIChatGenerator,HuggingFaceAPIGenerator,HuggingFaceLocalGenerator, andHuggingFaceLocalChatGenerator. - ›Introduces
LLMMessagesRoutercomponent (haystack.components.routers.llm_messages_router) that classifies and routesChatMessageobjects to named output connections using a generative LLM, supporting general-purpose and moderation-focused models like Llama Guard. - ›Introduces
HuggingFaceTEIRankercomponent for end-to-end reranking via the Text Embeddings Inference (TEI) API, supporting both self-hosted TEI services and Hugging Face Inference Endpoints. - ›Adds
AsyncHFTokenStreamingHandlerfor async streaming support inHuggingFaceLocalChatGenerator. - ›Makes
PipelineBase.validate_inputa public method so callers can validate pipeline connections before runtime without waiting forPipeline.run. - ›Adds
deserialize_component_inplacefunction for generic component deserialization that works with any component type. - ›All additional key-value pairs passed via
api_paramsinHuggingFaceAPIGeneratorandHuggingFaceAPIChatGeneratorare now forwarded to the underlying Inference Client constructors, enabling parameters liketimeout,headers, andprovider(e.g.,api_params={'provider': 'groq'}to route to a different inference provider). - ›Haystack's core modules now carry a
py.typedmarker and are fully type-annotated, enabling accurate static analysis in mypy and Pylance.
- ›Adds
- v2.14.2
Haystack v2.14.2 adds
raise_on_failureto OpenAI document embedders for stricter API error handling.└──▷ GET THIS VERSION$ git clone --branch v2.14.2 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.14.2
└──▷ USE ITFail fast during indexing pipelines when an OpenAI embedding API error occurs, so bad batches are never silently skipped.from haystack.components.embedders import OpenAIDocumentEmbedder embedder = OpenAIDocumentEmbedder(raise_on_failure=True)
- ›Adds
raise_on_failureboolean parameter toOpenAIDocumentEmbedderandAzureOpenAIDocumentEmbedder: when set to True, the component raises an exception on API errors instead of silently logging and continuing; defaults to False to preserve existing behavior.
- ›Adds
- v2.14.0
Haystack v2.14.0 adds async tool streaming, a new SentenceTransformers ranker, SuperComponent pipeline visualization expansion, and agent last_message output.
└──▷ GET THIS VERSION$ git clone --branch v2.14.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.14.0
└──▷ USE ITStream tool call results in real time from an Agent using the updatedstreaming_callbackparameter withprint_streaming_chunk.from haystack.components.agents import Agent from haystack.components.generators.chat import OpenAIChatGenerator from haystack.components.generators.utils import print_streaming_chunk from haystack.tools import ComponentTool from haystack.components.websearch import SerperDevWebSearch from haystack.dataclasses import ChatMessage web_search = ComponentTool(name="web_search", component=SerperDevWebSearch(top_k=5)) agent = Agent( chat_generator=OpenAIChatGenerator(model="gpt-4o-mini"), tools=[web_search], streaming_callback=print_streaming_chunk ) result = agent.run(messages=[ChatMessage.from_user("What happened in AI news today?")]) print(result["last_message"].text)Rank documents using the newSentenceTransformersSimilarityRankerwith the ONNX backend for faster CPU inference.from haystack.components.rankers import SentenceTransformersSimilarityRanker from haystack.utils.device import ComponentDevice from haystack.dataclasses import Document ranker = SentenceTransformersSimilarityRanker( model="sentence-transformers/all-MiniLM-L6-v2", device=ComponentDevice.from_str("cpu"), backend="onnx", ) ranker.warm_up() docs = [Document(content="Berlin"), Document(content="Sarajevo")] output = ranker.run(query="City in Germany", documents=docs) print(output["documents"])Expand SuperComponents in a pipeline diagram to see all internal components when debugging or documenting complex pipelines.from pathlib import Path from haystack import Pipeline from haystack.components.converters import MultiFileConverter from haystack.components.preprocessors import DocumentPreprocessor pipeline = Pipeline() pipeline.add_component("converter", MultiFileConverter()) pipeline.add_component("preprocessor", DocumentPreprocessor()) pipeline.connect("converter", "preprocessor") pipeline.draw(path=Path("expanded_pipeline.png"), super_component_expansion=True)- ›Adds
streaming_callbackparameter toToolInvokerand Agent to emit tool results in real time during tool invocation (results emitted after tool execution completes, not incrementally). - ›Adds
run_asyncmethod toToolInvokerclass to support asynchronous tool invocations, including streaming tool results. - ›Adds
last_messageoutput field to the Agent component for direct access to the final generatedChatMessage. - ›Adds
last_message_onlyparameter toAnswerBuilderto process only the final reply while preserving full conversation history in metadata. - ›Adds
all_messageskey to themetafield ofGeneratedAnswerobjects inAnswerBuilder, storing all generated messages for traceability.
+11 moreshow less
- ›Adds
super_component_expansion=Trueparameter to pipeline.draw() and pipeline.show() to expand SuperComponents into their constituent components in pipeline diagrams. - ›Introduces new
SentenceTransformersSimilarityRankercomponent supporting PyTorch, ONNX, and OpenVINO inference backends via abackendparameter; requiressentence-transformers>=4.1.0. - ›Adds
serialize_valueanddeserialize_valueutility methods for consistent value serialization across modules. - ›Moves State class to
agents.statemodule and adds serialization and deserialization capabilities. - ›Adds support for multiple outputs in
ConditionalRouter. - ›Updates
print_streaming_chunkto printToolCallinformation when present in a chunk's metadata. - ›Adds a
py.typedmarker file to Haystack, enabling PEP 561 type information for downstream projects and type checkers such as mypy. - ›Adds token usage metadata (prompt and completion token counts) to
ChatMessagereturned byHuggingFaceAPIChatGeneratorwhen streaming. - ›Adds a Protocol for
TextEmbedderto simplify creation of custom components or SuperComponents that accept anyTextEmbedderas an init parameter. - ›Adds Component signature validation method that reports mismatches between
runandrun_asyncmethod signatures to aid debugging of custom components. - ›Adds type hints to the
componentdecorator, improving Pyright/Pylance support and IDE docstring display.
└──▷ BREAKING ON UPGRADE- !The deprecated
deserialize_tools_inplaceutility function has been removed; replace all usages withdeserialize_tools_or_toolset_inplaceimported fromhaystack.tools.
- ›Adds
- v2.13.0
Haystack v2.13.0 adds async Agent support, a new Toolset class, the @super_component decorator, and broad http_client_kwargs proxy/SSL configuration.
└──▷ GET THIS VERSION$ git clone --branch v2.13.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.13.0
└──▷ USE ITRun an async web-search agent — useful in async web servers or notebooks where blocking calls are not acceptable.result = await web_search_agent.run_async( messages=[ChatMessage.from_user("Find information about Haystack by deepset")] )Group related tools into a Toolset and pass them to an Agent in one shot, simplifying tool management across large tool libraries.from haystack.tools import Toolset from haystack.components.agents import Agent from haystack.components.generators.chat import OpenAIChatGenerator math_toolset = Toolset([tool_one, tool_two]) agent = Agent( chat_generator=OpenAIChatGenerator(model="gpt-4o-mini"), tools=math_toolset )Build a custom hybrid retriever SuperComponent with minimal boilerplate using the @super_component decorator.from haystack import Pipeline, super_component from haystack.components.joiners import DocumentJoiner from haystack.components.embedders import SentenceTransformersTextEmbedder from haystack.components.retrievers import InMemoryBM25Retriever, InMemoryEmbeddingRetriever from haystack.document_stores.in_memory import InMemoryDocumentStore @super_component class HybridRetriever: def __init__(self, document_store: InMemoryDocumentStore): self.pipeline = Pipeline() self.pipeline.add_component("text_embedder", SentenceTransformersTextEmbedder()) self.pipeline.add_component("embedding_retriever", InMemoryEmbeddingRetriever(document_store)) self.pipeline.add_component("bm25_retriever", InMemoryBM25Retriever(document_store)) self.pipeline.add_component("document_joiner", DocumentJoiner(join_mode="reciprocal_rank_fusion")) self.pipeline.connect("text_embedder", "embedding_retriever") self.pipeline.connect("bm25_retriever", "document_joiner") self.pipeline.connect("embedding_retriever", "document_joiner")- ›Adds
run_asyncmethod to Agent, calling the underlyingChatGenerator'srun_asyncwhen available, enabling built-in async agent workflows. - ›Adds
http_client_kwargsparameter toOpenAIChatGenerator,AzureOpenAIChatGenerator,AzureOpenAIGenerator,OpenAIGenerator,DALLEImageGenerator,OpenAIDocumentEmbedder,OpenAITextEmbedder,AzureOpenAITextEmbedder,AzureOpenAIDocumentEmbedder, andRemoteWhisperTranscriberfor custom proxy and SSL configuration. - ›Introduces the Toolset class (importable from
haystack.tools) for grouping, filtering, serializing, and reusing multiple Tool instances as a single unit passable to Agent,ChatGenerator, andToolInvoker. - ›Adds
@super_componentdecorator (importable fromhaystack) so any class with apipelineattribute is automatically promoted to a full SuperComponent without manual wiring. - ›Adds two ready-made SuperComponents:
MultiFileConverterandDocumentPreprocessor, encapsulating common indexing pipeline logic.
+5 moreshow less
- ›Adds
run_asyncmethod toOpenAITextEmbedder,OpenAIDocumentEmbedder,AzureOpenAITextEmbedder,AzureOpenAIDocumentEmbedder,HuggingFaceAPIDocumentEmbedder, andHuggingFaceAPITextEmbedderfor async embedding. - ›Agent tracing now captures inputs and outputs of each
ChatGeneratorandToolInvokercall as dedicated child spans, enabling step-by-step visibility in tracers like Langfuse. - ›SuperComponents now support mapping non-leaf pipeline outputs to SuperComponent outputs via
output_mapping. - ›Adds
component_nameandcomponent_typeattributes toPipelineRuntimeError, plus a newPipelineComponentsBlockedErrorsubclass for pipelines where no components are unblocked. - ›Deprecates
deserialize_tools_inplaceutility function;deserialize_tools_or_toolset_inplaceshould be used instead (removal planned for Haystack 2.14.0).
└──▷ BREAKING ON UPGRADE- !The
api,api_key, andapi_paramsparameters of LLMEvaluator,ContextRelevanceEvaluator, andFaithfulnessEvaluatorhave been removed; use thechat_generatorparameter with aChatGeneratorconfigured for JSON output instead. - !The
generator_apiandgenerator_api_paramsparameters ofLLMMetadataExtractorand the LLMProvider enum have been removed; usechat_generatorinstead.
- ›Adds
- v2.12.0
Haystack v2.12.0 adds an Agent component with state management, SuperComponent for reusable pipelines, AutoMergingRetriever, and Azure AD token support.
└──▷ GET THIS VERSION$ git clone --branch v2.12.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.12.0
└──▷ USE ITWrap an existing RAG pipeline as aSuperComponentto expose a singlequeryinput across a retriever and prompt builder.from haystack import Pipeline, SuperComponent with open("rag_pipeline.yaml", "r") as f: pipeline = Pipeline.load(f) wrapper = SuperComponent( pipeline=pipeline, input_mapping={ "query": ["retriever.query", "prompt_builder.query"], }, output_mapping={"llm.replies": "replies"}, ) result = wrapper.run(query="What is the capital of France?") print(result["replies"])Split a CSV by individual rows instead of the default threshold, useful when each row is a self-contained record.from haystack.components.preprocessors import CSVDocumentSplitter splitter = CSVDocumentSplitter(split_mode="row-wise") result = splitter.run(documents=docs)
- ›Adds
outputs_to_stringparameter to Tool andComponentToolto customize how tool output is converted into a string before being passed back to theChatGeneratorin aChatMessage. - ›Adds
split_modeparameter toCSVDocumentSplitterto control splitting mode; supportsrow-wisesplitting in addition to the previous defaultthresholdbehavior. - ›Adds
link_formatparameter toDOCXToDocument(accepts'markdown'or'plain') to optionally include extracted hyperlink addresses in output Documents. - ›Adds
azure_ad_token_providerparameter toAzureOpenAIGenerator,AzureOpenAIChatGenerator,AzureOpenAITextEmbedder, andAzureOpenAIDocumentEmbedderfor Azure AD bearer-token authentication via a callable. - ›Introduces
default_azure_token_providerutility function inhaystack/utils/azure.pyas a serializable default token provider for Azure AD authentication.
+9 moreshow less
- ›Adds
run_asyncmethod toHuggingFaceLocalChatGenerator, usingThreadPoolExecutorinternally to return awaitable coroutines. - ›Adds
split_unit='token'support toRecursiveDocumentSplitter; uses theo200k_basetiktoken tokenizer (requirestiktokeninstalled). - ›Adds
chat_generatorinitialization parameter to LLMEvaluator,ContextRelevanceEvaluator, andFaithfulnessEvaluator, enabling any ChatGenerator instance (not only OpenAI-compatible) for evaluation. - ›New Agent component in
haystack.components.agentssupports tool-calling with any chat model, streaming viastreaming_callback, multipleexit_conditions, and astate_schemafor shared state across tools. - ›New
SuperComponentclass inhaystack.core.super_component.super_componentwraps any Haystack Pipeline into a reusable component withinput_mappingandoutput_mappingfor simplified interfaces. - ›New
AutoMergingRetrieverretrieval technique, used together withHierarchicalDocumentSplitter, implements auto-merging retrieval. - ›Adds asynchronous functionality and HTTP/2 support to
LinkContentFetcher. - ›New State dataclass with customizable schema for managing Agent state;
ToolInvokerextended to work with the new State. - ›Supports date/time handling via
arrowinChatPromptBuilder, consistent with existingPromptBuilderbehavior.
└──▷ BREAKING ON UPGRADE- !ChatMessage.to_dict() now returns keys
role,content,meta, andname— code that consumes the old dict format must be updated. - !The public
generatorattribute on LLMEvaluator,ContextRelevanceEvaluator, andFaithfulnessEvaluatoris replaced by_chat_generator; code referencing.generatorwill break. - !
to_pandas,comparative_individual_scores_report, andscore_reportare removed fromEvaluationRunResult— usedetailed_report,comparative_detailed_report, andaggregated_reportinstead. - !The Agent init parameter
exit_conditionis renamed toexit_conditions; existing code passingexit_condition=will break.
- ›Adds
- v2.11.0
Haystack v2.11.0 adds async run to all core chat generators and retrievers, a new MSGToDocument component, and ONNX/OpenVINO backend support for Sentence Transformers.
└──▷ GET THIS VERSION$ git clone --branch v2.11.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.11.0
└──▷ USE ITConvert an Outlook email (with attachments) into Haystack Documents for ingestion into a pipeline.from haystack.components.converters import MSGToDocument converter = MSGToDocument() result = converter.run(sources=["email.msg"]) print(result["documents"][0].meta) # sender, recipients, subject, etc. print(result["bytestream_outputs"]) # attachments as ByteStream objects
Disable connection type validation when prototyping a pipeline that mixes Optional and non-Optional socket types.from haystack import Pipeline pipeline = Pipeline(connection_type_validation=False) # Now connect Optional[str] -> str without a TypeError pipeline.connect("component_a.optional_output", "component_b.str_input")Run an async pipeline using OpenAIChatGenerator's new run_async method for concurrent throughput.import asyncio from haystack import AsyncPipeline from haystack.components.generators.chat import OpenAIChatGenerator from haystack.dataclasses import ChatMessage pipeline = AsyncPipeline() pipeline.add_component("llm", OpenAIChatGenerator(model="gpt-4o")) async def main(): result = await pipeline.run({"llm": {"messages": [ChatMessage.from_user("Hello")]}}) print(result) asyncio.run(main())- ›Adds
connection_type_validationparameter to Pipeline.__init__() (set to False to bypass type-checking on pipeline connections, e.g. connectingOptional[str]output tostrinput). - ›Adds
run_asyncmethod toOpenAIChatGenerator,AzureOpenAIChatGenerator,HuggingFaceAPIChatGenerator, andHuggingFaceLocalChatGenerator, enabling native async chat completion inside anAsyncPipeline. - ›Adds
run_asyncmethod toDocumentWriter, delegating towrite_documents_asyncon the backing document store. - ›Adds async support to
InMemoryDocumentStore,InMemoryBM25Retriever, andInMemoryEmbeddingRetriever. - ›Adds
backendparameter to Sentence Transformers components supportingtorch(default),onnx, andopenvinoinference backends.
+10 moreshow less
- ›New
MSGToDocumentcomponent converts Microsoft Outlook.msgfiles into Haystack Document objects, extracting sender, recipients, CC, BCC, and subject metadata and exposing attachments asByteStreamobjects. - ›Adds
store_full_pathinit variable toXLSXToDocumentto control whether the full source file path is stored in document metadata (defaults to False). - ›Exposes a configurable timeout parameter on
Pipeline.showandPipeline.drawmethods (default raised to 30 seconds) for the Mermaid rendering server. - ›
EvaluationRunResultcan now export results as JSON, a pandas DataFrame, or a CSV file. - ›Updates
ListJoinerso thatlist_typeis now optional, defaulting toList[Any]to combine any incoming lists without requiring strict type annotation. - ›Haystack now officially supports Python 3.13.
- ›Lazy importing reduces
import haystackCPU time to 2–5% of its previous cost and cuts per-component import CPU time by ~50%. - ›
FileTypeRouternow explicitly classifies.msgfiles with MIME typeapplication/vnd.ms-outlook. - ›
PDFMinerToDocumentnow detects and reports undecoded CID characters in extracted PDF text, flagging potential quality issues with non-standard fonts. - ›Deserialization now accepts standard typing shorthand without the
typing.prefix (e.g.,List[str]instead oftyping.List[str]).
└──▷ BREAKING ON UPGRADE- !The
ExtractedTableAnswerdataclass and thedataframefield on the Document dataclass (deprecated in 2.10.0) have been removed;pandasis no longer a required Haystack dependency. - !
AzureOCRDocumentConverterno longer produces Document objects with adataframefield; detected tables are now represented as CSV-formatted text in thecontentfield instead. - !Python 3.8 is no longer supported.
- ›Adds
- v2.10.0
Haystack v2.10.0 adds AsyncPipeline, universal tool calling, OpenAPIConnector, CSV document components, and local pipeline visualization.
└──▷ GET THIS VERSION$ git clone --branch v2.10.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.10.0
└──▷ USE ITInvoke a REST API endpoint directly from a pipeline using an OpenAPI spec, without an LLM generating the payload.from haystack.utils import Secret from haystack.components.connectors.openapi import OpenAPIConnector connector = OpenAPIConnector( openapi_spec="https://bit.ly/serperdev_openapi", credentials=Secret.from_env_var("SERPERDEV_API_KEY") ) response = connector.run(operation_id="search", parameters={"q": "Who was Nikola Tesla?"})- ›Adds
AsyncPipelineclass enabling concurrent component execution for pipelines with parallel branches (e.g. hybrid retrieval), with significant speed improvements over synchronous Pipeline.run(). - ›Adds
OpenAPIConnectorcomponent acceptingopenapi_specandcredentialsparameters for direct REST endpoint invocation from an OpenAPI spec without LLM-generated payloads. - ›Adds
CSVDocumentSplittercomponent that recursively splits CSV documents into structured sub-tables by empty rows and columns, with a configurable threshold — useful for Excel files containing multiple tables per sheet. - ›Adds
CSVDocumentCleanercomponent withremove_empty_rows,remove_empty_columns, andkeep_idparameters for cleaning CSV documents while preserving specified ignored rows and columns. - ›Adds
LLMMetadaExtractorcomponent for use in indexing pipelines to extract and enrich document metadata using an LLM based on a user-given prompt.
+7 moreshow less
- ›Adds
ListJoinercomponent that merges lists of values from multiple components into a single list. - ›Adds
completion_start_timemetadata field to track time-to-first-token (TTFT) in streaming responses from Hugging Face API and OpenAI (Azure). - ›Extends universal tool calling support to
AzureOpenAIChatGenerator,HuggingFaceLocalChatGenerator,AnthropicChatGenerator,CohereChatGenerator,AmazonBedrockChatGenerator, andVertexAIGeminiChatGeneratorwith no additional configuration required. - ›Enables local pipeline visualization via draw() or show() using a local Mermaid server with Docker, removing the need for an internet connection or external service.
- ›Enhances
SentenceTransformersDocumentEmbedderandSentenceTransformersTextEmbedderto accept additional parameters passed directly to the underlyingSentenceTransformer.encodemethod. - ›Adds
jsonschemaas a core dependency, used by Tool andJsonSchemaValidator. - ›Adds streaming callback
runparameter support for Hugging Face chat generators.
└──▷ BREAKING ON UPGRADE- !
DOCXToDocumentnow returns DOCX metadata inDocument.metaas a plain dictionary under the keydocxinstead of a DOCXMetadata dataclass. - !Removed the deprecated
NLTKDocumentSplitter; useDocumentSplitterinstead. - !Removed the deprecated
FUNCTIONrole fromChatRoleenum; useTOOLinstead. - !Removed the deprecated
ChatMessage.from_functionclass method; useChatMessage.from_toolinstead.
- ›Adds
- v2.9.0
Haystack v2.9.0 adds Tool/ToolInvoker abstractions, ComponentTool, RecursiveDocumentSplitter, XLSXToDocument, and StringJoiner.
└──▷ GET THIS VERSION$ git clone --branch v2.9.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.9.0
└──▷ USE ITWire an LLM to a live web search tool so the pipeline can answer questions requiring real-time information.from haystack import Pipeline from haystack.tools import ComponentTool from haystack.components.websearch import SerperDevWebSearch from haystack.utils import Secret from haystack.components.tools.tool_invoker import ToolInvoker from haystack.components.generators.chat import OpenAIChatGenerator from haystack.dataclasses import ChatMessage search = SerperDevWebSearch(api_key=Secret.from_env_var("SERPERDEV_API_KEY"), top_k=3) tool = ComponentTool( component=search, name="web_search", description="Search the web for current information on any topic" ) pipeline = Pipeline() pipeline.add_component("llm", OpenAIChatGenerator(model="gpt-4o-mini", tools=[tool])) pipeline.add_component("tool_invoker", ToolInvoker(tools=[tool])) pipeline.connect("llm.replies", "tool_invoker.messages") result = pipeline.run({"llm": {"messages": [ChatMessage.from_user("Who founded SpaceX?")]}}) print(result)- ›Adds Tool dataclass (importable from
haystack.tools) to represent callable tools for LLMs, plus acreate_tool_from_functionhelper and@tooldecorator for automatic name, description, and parameter generation. - ›Adds
ToolInvokercomponent (haystack.components.tools.tool_invoker) that executes LLM-prepared tool calls and returns results as aList[ChatMessage]with tool role; connects directly toOpenAIChatGeneratorandHuggingFaceAPIChatGeneratorviallm.replies→tool_invoker.messages. - ›Adds
ComponentTool(haystack.tools) to wrap any Haystack component (web search, document processing, custom) as an LLM-callable tool with automatic schema generation and input type conversion, supporting basic types, dataclasses, andList[Document]. - ›Adds
RecursiveDocumentSplitter(haystack.components.preprocessors) withsplit_length,split_overlap, andseparatorsparameters for recursive, separator-ordered text splitting. - ›Adds
XLSXToDocumentconverter that loads Excel files via Pandas + openpyxl, converting each sheet into a separate Document in CSV format.
+9 moreshow less
- ›Adds
store_full_pathparameter toPyPDFToDocumentandAzureOCRDocumentConverter__init__methods — True stores the full file path in document metadata, False stores only the filename. - ›Adds
StringJoinercomponent to collect strings from multiple pipeline components into a single list of strings. - ›Adds
from_openai_dict_formatclass method toChatMessagefor constructing aChatMessagefrom an OpenAI Chat API-format dictionary. - ›Adds
default_headersparameter toAzureOpenAIDocumentEmbedderandAzureOpenAITextEmbedder. - ›Adds
tokenargument toNamedEntityExtractorto support private Hugging Face models. - ›Merges
NLTKDocumentSplitterfunctionality intoDocumentSplitter:split_by='sentence'now uses NLTK-based sentence boundary detection; previous behaviour is available viasplit_by='period'. - ›Refactors
ChatMessagedataclass to support multiple content types (text, tool calls, tool call results); thecontentattribute is replaced by the newtextproperty. - ›Extends tool calling support to
HuggingFaceAPIChatGeneratorandOpenAIChatGenerator. - ›Improves callable serialization to support class methods and static methods; explicitly prohibits serialization of instance methods, lambdas, and nested functions.
└──▷ BREAKING ON UPGRADE- !The
contentattribute ofChatMessageis removed; use the newtextproperty to access textual content. Pipelines containingChatPromptBuilderserialized withhaystack-ai <= 2.9.0may fail to deserialize. - !The
converterinit argument is removed fromPyPDFToDocument; use the component's other init arguments or create a custom component. - !The
store_full_pathparameter default is changed to False in document converters — previously the full path was stored; now only the filename is stored unlessstore_full_path=Trueis set explicitly. - !The
SentenceWindowRetrieveroutput keycontext_documentsnow returnsList[Document](ordered bysplit_idx_start) instead ofList[List[Document]].
- ›Adds Tool dataclass (importable from
- v2.8.0
Haystack v2.8.0 adds DALLEImageGenerator, MetaFieldGroupingRanker, TTFT support, and new converter path controls.
└──▷ GET THIS VERSION$ git clone --branch v2.8.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.8.0
└──▷ USE ITGenerate an image from a text prompt using the new DALLEImageGenerator component.from haystack.components.generators import DALLEImageGenerator image_generator = DALLEImageGenerator() response = image_generator.run("Show me a picture of a black cat.") print(response)Enforce that every variable in a prompt template must be supplied at pipeline run time.from haystack.components.builders import PromptBuilder builder = PromptBuilder( template="Summarize the following: {{ text }} in {{ language }}", required_variables="*" )- ›Adds
store_full_pathparameter to__init__of JSONConverter,CSVToDocument,DOCXToDocument,HTMLToDocument,MarkdownToDocument,PDFMinerToDocument,PPTXToDocument,TikaDocumentConverter,PyPDFToDocument,AzureOCRDocumentConverter, andTextFileToDocument; set to False to store only the file name instead of the full path in document metadata (defaults to True). - ›Adds
required_variables='*'option toPromptBuilderandChatPromptBuilderto automatically mark all prompt template variables as required. - ›Adds optional parameters to
ConditionalRouterenabling default/fallback routing when certain inputs are absent at runtime. - ›New
DALLEImageGeneratorcomponent brings OpenAI DALL-E image generation into Haystack pipelines. - ›New
MetaFieldGroupingRankercomponent reorders documents by grouping them on metadata keys, useful for pre-processing before LLM ingestion.
+6 moreshow less
- ›Adds TTFT (Time-to-First-Token) support for OpenAI generators, capturing latency of first-token generation.
- ›Adds Maximum Margin Relevance (MMR) strategy to
SentenceTransformersDiversityRankerfor query-relevance and diversity-balanced document selection. - ›Adds split-by-line support to
DocumentSplitter. - ›Adds new initialization parameters to
PyPDFToDocumentfor customizing text extraction from PDF files. - ›Adds SSL verification toggle and custom certificate authority support when making function calls via
OpenAPI. - ›
OpenAIDocumentEmbeddernow continues processing remaining batches when a single batch fails embedding instead of stopping.
└──▷ BREAKING ON UPGRADE- !The
is_greedyargument has been removed from the@componentdecorator; replace Variadic inputs withGreedyVariadicin custom components.
- ›Adds
- v2.7.0
Haystack v2.7.0 adds LoggingTracer, StringJoiner, DOCX table extraction, and a reworked Pipeline.run() with better cycle support.
└──▷ GET THIS VERSION$ git clone --branch v2.7.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.7.0
└──▷ USE ITInspect every input and output flowing through a pipeline in real time during experimentation, without adding an external tracer.import logging from haystack import tracing from haystack.tracing.logging_tracer import LoggingTracer logging.basicConfig(format="%(levelname)s - %(name)s - %(message)s", level=logging.WARNING) logging.getLogger("haystack").setLevel(logging.DEBUG) tracing.tracer.is_content_tracing_enabled = True tracing.enable_tracing(LoggingTracer()) # Now run your pipeline — all spans appear in the log output pipeline.run({"text_embedder": {"text": "What is RAG?"}})- ›Introduces
LoggingTracer(importable fromhaystack.tracing.logging_tracer) that sends all pipeline traces to Python's logging system in real time; enable content tracing viatracing.tracer.is_content_tracing_enabled = Trueand activate with tracing.enable_tracing(LoggingTracer()). - ›Adds
additional_mimetypesparameter toFileTypeRoutercomponent, allowing users to supply extra MIME type mappings for correct file classification in environments like AWS Lambda. - ›Adds
streaming_callbackrun-time parameter toHuggingFaceAPIGeneratorandHuggingFaceLocalGeneratorfor per-chunk response callbacks. - ›Adds
validate_output_typeparameter toConditionalRouter; setting it to True enables runtime type-checking of route outputs, raisingValueErroron mismatch. - ›Adds
config_kwargsparameter toSentenceTransformersDocumentEmbedderandSentenceTransformersTextEmbedderfor passing additional options when loading model configuration.
+6 moreshow less
- ›Adds
metaparameter to FileTypeRouter.run(), automatically converting sources toByteStreamobjects with attached metadata for preprocessing/indexing pipelines. - ›Adds new
StringJoinercomponent to join strings from multiple components into a list of strings. - ›Enhances DOCX converter to extract table content in addition to paragraphs, supporting both CSV and Markdown output formats.
- ›Reworks Pipeline.run() internal logic for more reliable cycle handling and deterministic component execution order.
- ›Makes
window_sizea run-time parameter onSentenceWindowRetriever, overriding the constructor value per run. - ›Attaches each component tracing span to its parent pipeline run span, enabling concurrent multi-run tracing.
└──▷ BREAKING ON UPGRADE- !The
debug_pathinit argument has been removed from Pipeline. - !The
max_loops_allowedinit argument has been removed from Pipeline; usemax_runs_per_componentinstead. - !The
PipelineMaxLoopsexception has been removed; usePipelineMaxComponentRunsinstead. - !The
haystack.components.converters.pypdf.DefaultConverterclass has been removed; pipeline YAMLs using it must be updated to referencehaystack.components.converters.pdf.PDFToTextConverterwithconverter: null. - !Pipeline.connect() now raises
PipelineConnectErrorwhensenderandreceiverare the same component.
- ›Introduces
- v2.6.0
Haystack v2.6.0 adds JSONConverter, NLTKDocumentSplitter, zero-shot classifier, NDCG evaluator, and GreedyVariadic input type.
└──▷ GET THIS VERSION$ git clone --branch v2.6.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.6.0
└──▷ USE ITExtract structured fields from a nested JSON source into separate Documents using jq filtering — useful for ingesting datasets where each record should become its own Document.from haystack.components.converters import JSONConverter from haystack.dataclasses import ByteStream import json data = {"laureates": [{"firstname": "Enrico", "surname": "Fermi", "motivation": "discovery of nuclear reactions"}]} source = ByteStream.from_string(json.dumps(data)) converter = JSONConverter(jq_schema=".laureates[]", content_key="motivation", extra_meta_fields=["firstname", "surname"]) results = converter.run(sources=[source]) print(results["documents"][0].content) # 'discovery of nuclear reactions' print(results["documents"][0].meta) # {'firstname': 'Enrico', 'surname': 'Fermi'}Apply a domain-specific tokenization strategy (e.g., split on section headers) without subclassing DocumentSplitter.from haystack.components.preprocessors import DocumentSplitter from haystack.dataclasses import Document def split_on_headers(text: str) -> list[str]: import re return [s for s in re.split(r'(?=^#{1,3} )', text, flags=re.MULTILINE) if s.strip()] splitter = DocumentSplitter(split_by="function", splitting_function=split_on_headers) result = splitter.run(documents=[Document(content="# Intro\nHello\n## Details\nMore info")]) print([d.content for d in result["documents"]])- ›New JSONConverter component converts JSON files to Documents, with optional
jq_schemafiltering,content_keyselection, andextra_meta_fieldsextraction. - ›New
TransformersZeroShotDocumentClassifiercomponent enables binary and multi-label zero-shot document classification into user-defined classes using Hugging Face pre-trained models. - ›New
NLTKDocumentSplittercomponent splits documents by word count, sentence boundaries, and page breaks with multi-language support and configurable abbreviation handling. - ›New
CSVToDocumentcomponent loads CSV files as byte objects and produces Documents compatible withDocumentSplitter. - ›New
DocumentNDCGEvaluatorcomponent computes normalized discounted cumulative gain for retrieval evaluation when multiple ground-truth relevant documents exist and ranking order matters.
+9 moreshow less
- ›New
GreedyVariadicinput type replaces @component(is_greedy=True) — Pipeline runs the component as soon as any input arrives without waiting for all senders. - ›New
max_runs_per_componentinit argument on Pipeline replacesmax_loops_allowedwith clearer semantics; adds companionPipelineMaxComponentRunsexception. - ›
DocumentSplitternow accepts a custom splitting function viasplit_by='function'andsplitting_function=<callable>, where the callable takes a string and returns a list of strings. - ›
PromptBuildertemplates now support dynamic date injection via{% now '<timezone>' %}syntax, with optional offset arithmetic and strftime format strings. - ›Adds
azure_kwargsdictionary parameter to pass AzureOpenAI-supported parameters not explicitly defined in Haystack. - ›Exposes
default_headerson Azure components to forward custom HTTP headers such as APIM subscription keys. - ›Adds
usagemeta field withprompt_tokensandcompletion_tokenskeys toHuggingFaceAPIChatGeneratorresponses. - ›
SentenceTransformersDocumentEmbedderandSentenceTransformersTextEmbeddernow propagatemodel_max_lengthfromtokenizer_kwargsto the underlyingmax_seq_lengthof the SentenceTransformer model. - ›Adds batching during inference in
TransformerSimilarityRankerto prevent out-of-memory errors when ranking large document sets.
└──▷ BREAKING ON UPGRADE- !The legacy Haystack v1 filter syntax and operators (
$and,$or,$eq,$lt, etc.) are fully removed; only the new filter syntax is accepted. - !The default model for all OpenAI-backed components changes from
gpt-3.5-turbotogpt-4o-mini.
- ›New JSONConverter component converts JSON files to Documents, with optional
- v2.5.1
Haystack v2.5.1 adds
default_headersto Azure OpenAI generators for custom HTTP header injection.└──▷ GET THIS VERSION$ git clone --branch v2.5.1 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.5.1
- ›Adds
default_headersinit argument toAzureOpenAIGeneratorandAzureOpenAIChatGeneratorto pass custom HTTP headers on every request.
- ›Adds
- v2.5.0
Haystack v2.5.0 adds explicit
unsafe=Trueopt-in for dynamic code execution in routers and adapters, plus newmin_top_kfor TopPSampler and richer SentenceWindowRetriever output.└──▷ GET THIS VERSION$ git clone --branch v2.5.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.5.0
└──▷ USE ITEnable unsafe Jinja evaluation in a ConditionalRouter only when the template source is fully trusted, allowing ChatMessage or Document as output types.from haystack.components.routers import ConditionalRouter router = ConditionalRouter( routes=[ {"condition": "{{query | length > 50}}", "output": "{{chat_message}}", "output_type": "ChatMessage", "output_name": "long_query"} ], unsafe=True )Guarantee at least 3 documents from TopPSampler even when the probability-mass threshold would otherwise return fewer.from haystack.components.samplers import TopPSampler sampler = TopPSampler(p=0.90, min_top_k=3)
- ›Adds
unsafeargument toConditionalRouterandOutputAdapter; setunsafe=Trueto enable Jinja-template expressions that can return types such asChatMessage, Document, and Answer — disabled by default to prevent unintended remote code execution. - ›Adds
min_top_kparameter toTopPSamplerto guarantee a minimum number of returned documents when top-p sampling selects fewer than desired, backfilling with next-highest-scored documents. - ›
SentenceWindowRetrievernow outputs acontext_documentsfield alongsidecontext_windowsfor each entry inretrieved_documents, exposing the individual Document objects within each context window.
└──▷ BREAKING ON UPGRADE- !
ChatMessage.to_openai_formatmethod is removed; replace calls withhaystack.components.generators.openai_utils._convert_message_to_openai_format. - !The
debugparameter is removed fromPipeline.run; any code passingdebug=Truewill break. - !
SentenceWindowRetrievalis removed; replace withSentenceWindowRetriever.
- ›Adds
- v2.4.0
Haystack v2.4.0 adds local LLM support in evaluators, a new AnswerJoiner, and richer embedding controls via truncate_dim and precision.
└──▷ GET THIS VERSION$ git clone --branch v2.4.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.4.0
└──▷ USE ITRun faithfulness evaluation against a local LLM endpoint instead of OpenAI, with custom generation parameters.from haystack.components.evaluators import FaithfulnessEvaluator evaluator = FaithfulnessEvaluator( api_params={ "api_base_url": "http://localhost:11434/v1", "generation_kwargs": {"temperature": 0.0, "max_tokens": 512}, } ) result = evaluator.run(questions=["What is RAG?"], contexts=[["RAG combines retrieval and generation."]], responses=["RAG is a retrieval-augmented generation approach."])Produce compact, quantized embeddings for a large document corpus to reduce memory usage during semantic search.from haystack.components.embedders import SentenceTransformersDocumentEmbedder embedder = SentenceTransformersDocumentEmbedder( model="sentence-transformers/all-MiniLM-L6-v2", truncate_dim=128, precision="int8", )- ›Adds
api_paramsinit parameter toContextRelevanceEvaluatorandFaithfulnessEvaluator, enabling customgeneration_kwargsandapi_base_urlfor local LLM evaluation via any OpenAI-compatible endpoint. - ›Adds
truncate_dimparameter to Sentence Transformers Embedders for truncating embeddings, especially useful for Matryoshka Representation Learning models. - ›Adds
precisionparameter to Sentence Transformers Embedders for quantized embeddings, enabling corpus size reduction for semantic search. - ›Adds
model_kwargsandtokenizer_kwargstoTransformersSimilarityRanker,SentenceTransformersDocumentEmbedder, andSentenceTransformersTextEmbedder, supporting options likemodel_max_lengthandtorch_dtype. - ›Adds
unicode_normalizationparameter toDocumentCleaner, supporting NFC, NFD, NFKC, and NFKD normalization modes.
+6 moreshow less
- ›Adds
ascii_onlyparameter toDocumentCleanerto convert diacritic letters to ASCII equivalents and strip other non-ASCII characters. - ›Adds
max_retriesandtimeoutparameters toAzureOpenAIChatGenerator,AzureOpenAIDocumentEmbedder, andAzureOpenAITextEmbedderinitializations. - ›Allows
streaming_callbackto be passed at pipeline run time toOpenAIGeneratorandOpenAIChatGenerator, eliminating the need to recreate pipelines for streaming callbacks. - ›Enhanced filter application logic in retrievers to support merging of init-time and runtime filters with logical operators for complex metadata filtering combinations.
- ›New
AnswerJoinercomponent that combines multiple lists of Answer objects into a single list using Concatenate join mode. - ›Introduces a utility function to deserialize a generic Document Store from the
init_parametersof a serialized component.
└──▷ BREAKING ON UPGRADE- !
ContextRelevanceEvaluatornow returns only the list of relevant sentences per context (not all sentences), and scores 1 if any relevant sentence is found, 0 otherwise. - !
DynamicPromptBuilderandDynamicChatPromptBuilderare removed; usePromptBuilderandChatPromptBuilderinstead. - !
OutputAdapterandConditionalRoutercan no longer return user inputs. - !Multiplexer is removed; use
BranchJoinerinstead. - !Deprecated init parameters
extractor_typeandtry_othersare removed fromHTMLToDocument. - !
SentenceWindowRetrievalcomponent is renamed toSentenceWindowRetriever. - !Utility functions
serialize_callback_handleranddeserialize_callback_handlerare removed; useserialize_callableanddeserialize_callableinstead.
- ›Adds
- v2.3.0
Haystack v2.3.0 adds experimental package, five new components, and distribution-based rank fusion
└──▷ GET THIS VERSION$ git clone --branch v2.3.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.3.0
└──▷ USE ITShare a single in-memory document store between a writer pipeline and a retrieval pipeline without duplicating data.from haystack.document_stores.in_memory import InMemoryDocumentStore from haystack.dataclasses import Document index = "shared_knowledge_base" store_writer = InMemoryDocumentStore(index=index) store_retriever = InMemoryDocumentStore(index=index) store_writer.write_documents([Document(content="Haystack is an LLM framework.")]) print(store_retriever.count_documents()) # 1 — same memory
Drop documents missing a ranking field instead of letting them pollute scored results.from haystack.components.rankers import MetaFieldRanker ranker = MetaFieldRanker(meta_field="score", missing_meta="drop") result = ranker.run(documents=docs) print(result["documents"]) # only documents that have 'score' metadata
- ›Introduces the
haystack-experimentalpackage (pip install haystack-experimental), importable viafrom haystack_experimental.component_type import Component, shipping three initial components:OpenAIFunctionCaller,OpenAPITool, andEvaluationHarness. - ›Adds
OpenAIFunctionCaller(inhaystack-experimental) to call LLM-returned functions after Chat Generators. - ›Adds
OpenAPITool(inhaystack-experimental) to translate natural-language instructions into structured payloads for RESTful OpenAPI endpoints. - ›Adds
EvaluationHarness(inhaystack-experimental) to wrap pipelines and complex evaluation tasks into a single runnable component. - ›Adds
TransformersTextRoutercomponent, which uses a Transformers text-classification pipeline to route text inputs to different output connections based on model labels.
+17 moreshow less
- ›Adds
SentenceWindowRetrievalcomponent for sentence-window retrieval, fetching surrounding context documents for a given chunk from the document store. - ›Adds
DOCXToDocumentconverter component (usespython-docx) to convert Docx files into Haystack Documents. - ›Adds a PPTX-to-Document converter (uses
python-pptx) that extracts text from each slide, separating slides with a page break\fsoDocumentSplittercan split by slide. - ›Adds Distribution-Based Score Fusion (DBSF) as a new ranking mode in
JoinDocuments. - ›Adds
missing_metaparameter toMetaFieldRankercontrolling handling of documents that lack the ranked meta field; supported values are'bottom','top', and'drop'. - ›Adds
indexparameter toInMemoryDocumentStoreto enable memory sharing between multiple instances using the same index name. - ›Adds
filter_policyinit parameter toInMemoryBM25RetrieverandInMemoryEmbeddingRetrieverwith'replace'or'merge'options for combining runtime and initial filters. - ›Adds custom Jinja2 filter callables support to
ConditionalRoutervia user-supplied filter callables accessible in condition expressions. - ›Adds
split_idandsplit_overlapsupport toDocumentSplitterfor finer control over the splitting process. - ›Adds
save_to_diskandwrite_to_diskserialization methods toInMemoryDocumentStore. - ›Adds
remove_componentmethod toPipelineBaseto delete components and their connections from a pipeline. - ›Adds
max_retriesandtimeoutparameters toAzureOpenAIGenerator,AzureOpenAIChatGenerator,AzureOpenAITextEmbedder, andAzureOpenAIDocumentEmbedder; values fall back toOPENAI_MAX_RETRIES(default 5) andOPENAI_TIMEOUT(default 30) environment variables. - ›Adds support for structlog context variables to structured logging.
- ›Enables
AnswerBuilderto acceptChatMessageobjects as input in addition to strings, with metadata automatically added to the answer. - ›Expands
LinkContentFetchercontent-type support to include glob patterns for text, application, audio, and video types via a flexible handler resolution mechanism. - ›Pipeline serialization to YAML now supports tuples as field values.
- ›Extends HuggingFace API components to accept both
HF_API_TOKENandHF_TOKENenvironment variable names.
└──▷ BREAKING ON UPGRADE- !
trafilaturais no longer installed automatically; runpip install trafilaturamanually to continue usingHTMLToDocument. - !The
converter_nameparameter has been removed fromPyPDFToDocument; use theconverterinit parameter with an instance implementing thePyPDFConverterprotocol (convert,to_dict,from_dict) instead, or rely on the providedDefaultConverterclass. - !
HuggingFaceTEITextEmbedderandHuggingFaceTEIDocumentEmbedderhave been removed; replace withHuggingFaceAPITextEmbedderandHuggingFaceAPIDocumentEmbedder. - !
HuggingFaceTGIGeneratorandHuggingFaceTGIChatGeneratorhave been removed; replace withHuggingFaceAPIGeneratorandHuggingFaceAPIChatGenerator.
- ›Introduces the
- v2.2.4
Haystack v2.2.4 adds
filter_policyto in-memory retrievers for flexible runtime filter control.└──▷ GET THIS VERSION$ git clone --branch v2.2.4 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.2.4
└──▷ USE ITUsefilter_policy='merge'on anInMemoryBM25Retrieverso that runtime filters are combined with the retriever's initial filters rather than overwriting them.from haystack.components.retrievers.in_memory import InMemoryBM25Retriever retriever = InMemoryBM25Retriever( document_store=document_store, filter_policy='merge' )- ›Introduces
filter_policyinit parameter forInMemoryBM25RetrieverandInMemoryEmbeddingRetriever, accepting'replace'or'merge'to control how runtime filters interact with initial filters. - ›Adds
apply_filter_policyfunction to standardize filter-policy application across all document store-specific retrievers, enabling consistentreplace/mergebehavior.
- ›Introduces
- v1.26.0
Haystack 1.26 adds split-by-page chunking, new OpenAI embedding models, Llama3/Mistral/Claude 3 on Bedrock, and local OpenAI-compatible endpoint support.
└──▷ GET THIS VERSION$ git clone --branch v1.26.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.26.0
└──▷ USE ITRun RAG against a local LM Studio endpoint instead of the OpenAI cloud, keeping data on-premises.prompt_node = PromptNode( model_name_or_path='gpt-3.5-turbo', api_key='<your-key>', api_base='http://localhost:1234/v1' )Allow a large-batch document conversion job to skip bad files and continue rather than raising on the first failure.converter = PDFToTextConverter(raise_on_failure=False) docs = converter.convert(file_paths=my_file_list)
- ›Adds
raise_on_failureflag toBaseConverterso large batch processes can continue past per-document exceptions instead of aborting. - ›Adds
split_by='page'option to the preprocessor, enabling document chunking by page break. - ›Adds support for OpenAI embedding models
text-embedding-3-largeandtext-embedding-3-small. - ›Adds
API_BASEoptional parameter toPromptNodeandPromptModel, enabling RAG against any local OpenAI-compatible endpoint (e.g.http://localhost:1234/v1, LM Studio). - ›Supports Llama3 models on AWS Bedrock.
+5 moreshow less
- ›Supports MistralAI and new Claude 3 models on AWS Bedrock.
- ›Supports Cohere Command R models via Transformers upgrade to version 4.39.3.
- ›Supports Phi-2 and Qwen2 models and improved quantization via Transformers upgrade to version 4.37.2.
- ›Supports gated repos for Hugging Face inference.
- ›Adds a pre-flight check verifying that embedding dimensions in the FAISS Document Store and retriever match before running embedding calculations.
└──▷ BREAKING ON UPGRADE- !The utility functions
fetch_archive_from_http,build_pipeline, andadd_example_datahave been removed from Haystack. - !
PDFToTextConverterno longer supports PyMuPDF; it now always usesxpdfby default. To keep using PyMuPDF you must create a custom node.
- ›Adds
- v1.26.0-rc1
Haystack v1.26.0-rc1 adds Llama3/MistralAI/Claude 3 on AWS Bedrock, Cohere Command R support, and page-based document splitting.
└──▷ GET THIS VERSION$ git clone --branch v1.26.0-rc1 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.26.0-rc1
└──▷ USE ITChunk a multi-page PDF by page boundary rather than by word or sentence count.from haystack.nodes import PreProcessor preprocessor = PreProcessor(split_by='page', split_length=1) pages = preprocessor.process(documents)
- ›Adds
raise_on_failureflag toBaseConverterclass so large batch processes can continue past individual conversion exceptions. - ›Adds
split_by='page'option to the preprocessor for chunking documents by page break. - ›Adds support for OpenAI embedding models
text-embedding-3-largeandtext-embedding-3-small. - ›Adds
API_BASEas an optional parameter toPromptNodeandPromptModel, enabling RAG against any OpenAI-compatible local endpoint (e.g.http://localhost:1234/v1via LM Studio). - ›Adds a dimension-mismatch check between the FAISS Document Store and retriever before running embedding calculations, surfacing misconfiguration early.
+5 moreshow less
- ›Adds support for Llama3 models on AWS Bedrock.
- ›Adds support for MistralAI and new Claude 3 models on AWS Bedrock.
- ›Adds support for Cohere Command R models via Transformers upgrade to 4.39.3.
- ›Adds support for gated repos on Hugging Face inference.
- ›Updates context windows for OpenAI GPT models to reflect current limits.
└──▷ BREAKING ON UPGRADE- !The utility functions
fetch_archive_from_http,build_pipeline, andadd_example_datahave been removed from Haystack; callers must replace them with alternatives. - !
PDFToTextConverterno longer supports PyMuPDF — it now always usesxpdfby default. To retain PyMuPDF support you must implement a custom node.
- ›Adds
- v2.2.0
Haystack v2.2.0 adds BranchJoiner, runtime template swapping,
OPENAI_TIMEOUT/OPENAI_MAX_RETRIESenv vars, and DocumentSplitter threshold control.└──▷ GET THIS VERSION$ git clone --branch v2.2.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.2.0
└──▷ TRY ITControl OpenAI request timeout and retry budget without touching code — useful in flaky-network or rate-limited environments.$ export OPENAI_TIMEOUT=30 export OPENAI_MAX_RETRIES=5 python my_pipeline.pyAvoid tiny trailing chunks when splitting long documents by setting a minimum viable chunk size.from haystack.components.preprocessors import DocumentSplitter splitter = DocumentSplitter( split_by="word", split_length=200, split_threshold=50 )Preserve original document IDs through a cleaning step so downstream deduplication or tracing still works.from haystack.components.preprocessors import DocumentCleaner cleaner = DocumentCleaner(keep_id=True)
- ›Adds
OPENAI_TIMEOUTandOPENAI_MAX_RETRIESenvironment variables (also settable at__init__) to configure timeout and retry behaviour across OpenAI components. - ›Adds
split_thresholdparameter toDocumentSplitter— chunks smaller than the threshold are concatenated with the previous chunk to avoid meaninglessly small splits. - ›Adds
keep_idoptional attribute toDocumentCleaner— when True, document IDs are preserved unchanged after cleanup. - ›Adds
top_kparameter to DocumentJoiner.run(), letting callers cap the number of returned documents at query time. - ›Introduces
BranchJoineras a new component with the same interface as the now-deprecated Multiplexer, with clearer semantics.
+7 moreshow less
- ›
AzureOpenAIGeneratorandAzureOpenAIChatGeneratornow accept atimeoutparameter for the underlyingAzureOpenAIclient. - ›
ChatPromptBuildernow supports runtime template changes, supersedingDynamicChatPromptBuilder. - ›
PromptBuildernow supports runtime template changes, supersedingDynamicPromptBuilder. - ›Re-implements
InMemoryDocumentStoreBM25 search with incremental indexing, eliminating full index rebuilds per query and removing thehaystack_bm25dependency. - ›LLM-based evaluators (e.g. Faithfulness,
ContextRelevance) initialised withraise_on_failure=Falsenow set the sample score toNaNand emit a warning instead of raising an exception when an LLM call fails or returns invalid JSON. - ›Switches
HTMLToDocumentHTML conversion backend fromboilerpy3totrafilaturafor more robust and actively maintained parsing. - ›Improves MIME type handling by setting MIME types directly on
ByteStreamobjects, making type data consistently accessible across document format routing.
└──▷ BREAKING ON UPGRADE- !Multiplexer is renamed to
BranchJoiner; existing code must rename all occurrences of Multiplexer toBranchJoinerand update imports accordingly.
- ›Adds
- v2.1.0
Haystack v2.1.0 adds 8 evaluator components, sparse embedding support, per-component output inspection, and new HuggingFace API generators.
└──▷ GET THIS VERSION$ git clone --branch v2.1.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.1.0
└──▷ USE ITInspect intermediate retriever and LLM outputs during a pipeline run without modifying the pipeline definition.pipe.run(data, include_outputs_from={"prompt_builder", "llm", "retriever"})Evaluate retrieval quality with mean average precision across multiple queries.from haystack.components.evaluators import DocumentMAPEvaluator from haystack import Document evaluator = DocumentMAPEvaluator() result = evaluator.run( ground_truth_documents=[ [Document(content="France")], [Document(content="9th century"), Document(content="9th")], ], retrieved_documents=[ [Document(content="France")], [Document(content="9th century"), Document(content="10th century"), Document(content="9th")], ], ) print(result["score"]) # 0.9166666666666666Build a sparse embedding retrieval pipeline using SPLADE for improved keyword-sensitive semantic search.from haystack import Pipeline from haystack_integrations.components.retrievers.qdrant import QdrantSparseEmbeddingRetriever from haystack_integrations.components.embedders.fastembed import FastembedSparseTextEmbedder sparse_text_embedder = FastembedSparseTextEmbedder(model="prithvida/Splade_PP_en_v1") sparse_retriever = QdrantSparseEmbeddingRetriever(document_store=document_store) query_pipeline = Pipeline() query_pipeline.add_component("sparse_text_embedder", sparse_text_embedder) query_pipeline.add_component("sparse_retriever", sparse_retriever) query_pipeline.connect("sparse_text_embedder.sparse_embedding", "sparse_retriever.query_sparse_embedding")- ›Adds
include_outputs_fromparameter to pipeline.run() accepting a set of component names, returning intermediate outputs for those components in the final pipeline output dictionary. - ›Adds
truncateandnormalizeparameters toHuggingFaceTEITextEmbedderandHuggingFaceTEIDocumentEmbedderfor controlling embedding truncation and normalization. - ›Adds
trust_remote_codeparameter toSentenceTransformersDocumentEmbedderandSentenceTransformersTextEmbedderto allow custom models and scripts. - ›Adds
streaming_callbackparameter toHuggingFaceLocalGeneratorfor handling streaming responses. - ›Adds
try_othersparameter (default True) toHTMLToDocumentto attempt multiple extractors in priority order on extraction failure.
+15 moreshow less
- ›Adds
dimensionsparameter toAzureOpenAITextEmbedderandAzureOpenAIDocumentEmbedderto support new embedding models such astext-embedding-3-smallandtext-embedding-3-large. - ›Adds
converterparameter toPyPDFToDocumentfor custom PDF converter classes implementing thePyPDFConverterprotocol withconvert,to_dict, andfrom_dictmethods. - ›Adds support for pre-init hook callbacks during pipeline deserialization, allowing inspection and modification of component initialization parameters before
__init__is called. - ›Introduces
AnswerExactMatchEvaluator,ContextRelevanceEvaluator,DocumentMAPEvaluator,DocumentMRREvaluator,DocumentRecallEvaluator,FaithfulnessEvaluator, LLMEvaluator, and SASEvaluator components for model-based and statistical RAG pipeline evaluation. - ›Introduces
SparseEmbeddingclass for storing sparse vector representations of documents, enabling sparse embedding retrieval pipelines (e.g., SPLADE viaQdrantSparseEmbeddingRetrieverandFastembedSparseTextEmbedder). - ›Introduces
HuggingFaceAPIChatGenerator,HuggingFaceAPIDocumentEmbedder,HuggingFaceAPIGenerator, andHuggingFaceAPITextEmbeddercomponents supporting the free Serverless Inference API, paid Inference Endpoints, and self-hosted Text Generation Inference. - ›Adds
SentenceTransformersDiversityRankercomponent that reorders documents to maximize semantic diversity using sentence-transformer embeddings. - ›Adds
ZeroShotTextRoutercomponent that uses a HuggingFace NLI model to classify and route texts based on user-provided labels. - ›Enhances
FileTypeRouterwith regex pattern support for MIME types, enabling granular file routing by broad categories or specific MIME type patterns. - ›Enhances
PromptBuilderto specify and enforce required variables in prompt templates. - ›Enhances
DynamicChatPromptBuilderto allow all user and system messages to be templated with provided variables. - ›Enhances
AzureOCRDocumentConverterwith advanced table and text handling: extracting preceding/following context for tables, merging multiple column headers, and single-column page layout for text. - ›Now
DocumentSplitteradds apage_numberfield to the metadata of all output documents tracking the originating page of the source document. - ›Sets
max_new_tokensdefault to 512 in HuggingFace generators. - ›In Jupyter notebooks, Pipeline now displays a textual representation by default; call the
showmethod to display the pipeline image.
└──▷ BREAKING ON UPGRADE- !The
converter_nameparameter inPyPDFToDocumentis deprecated and will be removed in v2.3.0; use theconverterparameter instead. - !
HuggingFaceTGIChatGeneratoris deprecated and will be removed in v2.3.0; useHuggingFaceAPIChatGeneratorinstead. - !
HuggingFaceTGIGeneratoris deprecated and will be removed in v2.3.0; useHuggingFaceAPIGeneratorinstead. - !
HuggingFaceTEIDocumentEmbedderis deprecated and will be removed in v2.3.0; useHuggingFaceAPIDocumentEmbedderinstead. - !
HuggingFaceTEITextEmbedderis deprecated and will be removed in v2.3.0; useHuggingFaceAPITextEmbedderinstead. - !In Jupyter notebooks, Pipeline no longer displays its image automatically on render; call pipeline.show() explicitly to display it.
- ›Adds
- v2.1.0-rc2
Haystack v2.1.0-rc2 adds 8 evaluator components, sparse embedding support, per-component output inspection, and new HuggingFace API generators.
└──▷ GET THIS VERSION$ git clone --branch v2.1.0-rc2 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.1.0-rc2
└──▷ USE ITInspect intermediate outputs from specific components after a pipeline run to debug retrieval or generation steps.pipe.run(data, include_outputs_from=["prompt_builder", "llm", "retriever"])
Evaluate retrieved documents against ground truth using mean average precision scoring.from haystack.components.evaluators import DocumentMAPEvaluator evaluator = DocumentMAPEvaluator() result = evaluator.run( ground_truth_documents=[[Document(content="France")], [Document(content="9th century")]], retrieved_documents=[[Document(content="France")], [Document(content="9th century"), Document(content="10th century")]], ) print(result["score"])Use sparse embedding retrieval (SPLADE) in a query pipeline with Qdrant and FastEmbed.from haystack import Pipeline from haystack_integrations.components.retrievers.qdrant import QdrantSparseEmbeddingRetriever from haystack_integrations.components.embedders.fastembed import FastembedSparseTextEmbedder sparse_text_embedder = FastembedSparseTextEmbedder(model="prithvida/Splade_PP_en_v1") sparse_retriever = QdrantSparseEmbeddingRetriever(document_store=document_store) query_pipeline = Pipeline() query_pipeline.add_component("sparse_text_embedder", sparse_text_embedder) query_pipeline.add_component("sparse_retriever", sparse_retriever) query_pipeline.connect("sparse_text_embedder.sparse_embedding", "sparse_retriever.query_sparse_embedding")- ›Adds
include_outputs_fromparameter to pipeline.run() accepting a set of component names whose intermediate outputs are returned in the final pipeline output dictionary. - ›Adds
trust_remote_codeparameter toSentenceTransformersDocumentEmbedderandSentenceTransformersTextEmbedderfor allowing custom models and scripts. - ›Adds
truncateandnormalizeparameters toHuggingFaceTEITextEmbedderfor truncation and normalization of embeddings. - ›Adds
streaming_callbackparameter toHuggingFaceLocalGeneratorfor handling streaming responses. - ›Adds
dimensionsparameter toAzureOpenAITextEmbedderandAzureOpenAIDocumentEmbedderto support new embedding models includingtext-embedding-3-smallandtext-embedding-3-large.
+15 moreshow less
- ›Adds
try_othersparameter toHTMLToDocument(default True) to attempt multiple extractors in priority order when one fails. - ›Introduces new
HuggingFaceAPIChatGenerator,HuggingFaceAPIDocumentEmbedder,HuggingFaceAPIGenerator, andHuggingFaceAPITextEmbeddercomponents supporting the free Serverless Inference API, paid Inference Endpoints, and self-hosted Text Generation Inference. - ›Adds 8 new evaluation components:
AnswerExactMatchEvaluator,ContextRelevanceEvaluator,DocumentMAPEvaluator,DocumentMRREvaluator,DocumentRecallEvaluator,FaithfulnessEvaluator, LLMEvaluator, and SASEvaluator for model-based and statistical RAG pipeline evaluation. - ›Introduces new
SparseEmbeddingclass for storing sparse vector representations of documents, enabling sparse embedding retrieval techniques such as SPLADE. - ›Adds
SentenceTransformersDiversityRankercomponent that orders documents to maximize overall diversity using semantic embeddings. - ›Adds
ZeroShotTextRoutercomponent that uses a HuggingFace NLI model to classify and route texts based on provided labels. - ›Adds support for callbacks during pipeline deserialization, including a pre-init hook to inspect and modify component initialization parameters before
__init__is invoked. - ›Adds
page_numberfield to the metadata of all output documents fromDocumentSplitterto track the originating page. - ›Adds regex pattern support for MIME types in
FileTypeRouterfor granular file routing. - ›Enhances
PromptBuilderto specify and enforce required variables in prompt templates. - ›Enhances
AzureOCRDocumentConverterwith advanced table and text handling including preceding/following context extraction for tables, merging multiple column headers, and single-column page layout support. - ›Enhances
DynamicChatPromptBuilderto allow all user and system messages to be templated with provided variables. - ›Refactors
PyPDFToDocumentto support custom PDF converters via theconverterparameter; converters implement thePyPDFConverterprotocol withconvert,to_dict, andfrom_dictmethods. - ›Sets
max_new_tokensdefault to 512 in HuggingFace generators. - ›In Jupyter notebooks, Pipeline now displays a textual representation by default; use the
showmethod on the Pipeline object to render the image.
- ›Adds
- v2.1.0-rc1
Haystack v2.1.0-rc1 adds diversity ranking, six new evaluators, four unified HuggingFace API components, sparse embeddings, and a zero-shot text router.
└──▷ GET THIS VERSION$ git clone --branch v2.1.0-rc1 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.1.0-rc1
└──▷ USE ITRoute files to different pipeline branches using regex MIME-type patterns, avoiding the need to enumerate every subtype explicitly.from haystack.components.routers import FileTypeRouter from pathlib import Path router = FileTypeRouter(mime_types=[r"text/.*", r"application/(pdf|json)"]) result = router.run(sources=[Path("report.pdf"), Path("notes.txt"), Path("data.json"), Path("image.png")]) for mime_type, files in result.items(): print(f"MIME Type: {mime_type}, Files: {[str(f) for f in files]}")Score faithfulness of RAG answers at evaluation time to detect hallucinations against retrieved context.from haystack.components.evaluators import FaithfulnessEvaluator evaluator = FaithfulnessEvaluator() result = evaluator.run( questions=["What is the capital of France?"], contexts=[["Paris is the capital and largest city of France."]], predicted_answers=["The capital of France is Paris."] ) print(result["score"]) # float between 0 and 1Stream tokens from a local Hugging Face model during generation instead of waiting for the full response.from haystack.components.generators import HuggingFaceLocalGenerator def my_callback(token): print(token, end="", flush=True) generator = HuggingFaceLocalGenerator( model="google/flan-t5-large", streaming_callback=my_callback ) generator.warm_up() generator.run(prompt="Summarize the OWASP Top 10 in three sentences.")- ›Adds
truncateandnormalizeparameters toHuggingFaceTEITextEmbedderfor controlling truncation and normalization of embeddings. - ›Adds
trust_remote_codeparameter toSentenceTransformersDocumentEmbedderandSentenceTransformersTextEmbedderto allow custom models and scripts. - ›Adds
streaming_callbackparameter toHuggingFaceLocalGeneratorto handle streaming responses. - ›Adds
dimensionsparameter toAzureOpenAITextEmbedderandAzureOpenAIDocumentEmbedderto support newer embedding models such astext-embedding-3-smallandtext-embedding-3-large. - ›Adds
try_othersparameter toHTMLToDocument(defaulttrue) to fall back through multiple extractors in priority order on failure.
+25 moreshow less
- ›Introduces
HuggingFaceAPIChatGenerator, a unified chat-format text-generation component supporting the free Serverless Inference API, paid Inference Endpoints, and self-hosted Text Generation Inference — intended to replaceHuggingFaceTGIChatGenerator. - ›Introduces
HuggingFaceAPIGenerator, a unified text-generation component supporting Serverless Inference API, Inference Endpoints, and self-hosted TGI — intended to replaceHuggingFaceTGIGenerator. - ›Introduces
HuggingFaceAPIDocumentEmbedder, a unified document-embedding component supporting Serverless Inference API, Inference Endpoints, and self-hosted Text Embeddings Inference — intended to replaceHuggingFaceTEIDocumentEmbedder. - ›Introduces
HuggingFaceAPITextEmbedder, a unified string-embedding component supporting Serverless Inference API, Inference Endpoints, and self-hosted Text Embeddings Inference — intended to replaceHuggingFaceTEITextEmbedder. - ›Adds
SentenceTransformersDiversityRanker, which reorders documents to maximize semantic diversity using sentence-transformer embeddings. - ›Adds
ContextRelevanceEvaluatorcomponent that uses an LLM to score (0–1) how relevant retrieved documents are to a question in a RAG pipeline. - ›Adds
FaithfulnessEvaluatorcomponent that scores (0–1) the proportion of statements in an LLM answer that can be inferred from retrieved documents. - ›Adds LLMEvaluator component that leverages the OpenAI API to evaluate pipeline outputs.
- ›Adds
DocumentMAPEvaluatorcomponent to calculate mean average precision of retrieved documents. - ›Adds
DocumentMRREvaluatorcomponent to calculate mean reciprocal rank of retrieved documents. - ›Adds
DocumentRecallEvaluatorcomponent to calculate single-hit or multi-hit recall for retrieved documents. - ›Adds SASEvaluator component to calculate Semantic Answer Similarity of LLM-generated answers.
- ›Adds
EvaluationRunResultdataclass to wrap, transform, and visualize results from an evaluation pipeline. - ›Introduces
SparseEmbeddingclass for storing sparse vector representations of documents, laying groundwork for Sparse Embedding Retrieval. - ›Adds Zero Shot Text Router that uses an NLI model from Hugging Face to classify and route texts by label.
- ›Extends
FileTypeRouterwith regex pattern matching for MIME types, enabling granular file routing such asr'text/.*'orr'application/(pdf|json)'. - ›Adds support for callbacks during pipeline deserialization, including a pre-init hook to inspect and modify component initialization parameters before
__init__is called. - ›Enables
pipeline.runto accept a set of component names whose intermediate outputs are included in the final pipeline output dictionary. - ›Makes
Pipeline.inputsandPipeline.outputsoptionally include connected component input/output sockets. - ›Refactors
PyPDFToDocumentto support custom PDF converters via thePyPDFConverterprotocol (requiringconvert,to_dict, andfrom_dictmethods), withDefaultConverteras the built-in implementation. - ›Enhances
PromptBuilderto specify and enforce required variables in prompt templates. - ›Enhances
DynamicChatPromptBuilderto allow all user and system messages to be templated with provided variables. - ›Enhances
AzureOCRDocumentConverterwith advanced table and text handling: preceding/following context extraction for tables, merged multi-column headers, and single-column page layout for text. - ›Sets
max_new_tokensdefault to512in Hugging Face generators. - ›Now
DocumentSplitteradds apage_numberfield to the metadata of all output documents to track original page provenance.
- ›Adds
- v1.25.3
Haystack v1.25.3 adds Llama 3, Mistral AI, Claude 3, and Cohere Command R model support on AWS Bedrock.
└──▷ GET THIS VERSION$ git clone --branch v1.25.3 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.25.3
- ›Supports Llama 3 models on AWS Bedrock.
- ›Supports Mistral AI and new Claude 3 models on AWS Bedrock.
- ›Upgrades
transformersto version 4.39.3, enabling support for Cohere Command R models.
- v2.0.1
Haystack v2.0.1 adds streaming support to HuggingFaceLocalGenerator and introduces a new SparseEmbedding class.
└──▷ GET THIS VERSION$ git clone --branch v2.0.1 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.0.1
- ›Adds
streaming_callbackparameter toHuggingFaceLocalGeneratorto handle streaming responses. - ›Introduces new
SparseEmbeddingclass for storing sparse vector representations of a Document, laying groundwork for Sparse Embedding Retrieval with forthcoming Sparse Embedders and Sparse Embedding Retrievers.
- ›Adds
- v1.25.2
Haystack v1.25.2 adds
response_format,seed, and prompt-truncation toggle to OpenAI/Azure invocation layers.└──▷ GET THIS VERSION$ git clone --branch v1.25.2 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.25.2
- ›Adds
response_formatandseedparameters to the OpenAI and Azure OpenAI invocation layers, enabling structured output control and reproducible sampling. - ›Adds a boolean parameter to toggle prompt truncation in invocation layers, giving callers explicit control over whether long prompts are silently cut.
- ›Adds
- v2.0.0
Haystack 2.0 is a full rewrite introducing composable pipelines, typed components, and a new
haystack-aipackage.└──▷ GET THIS VERSION$ git clone --branch v2.0.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v2.0.0
└──▷ USE ITBuild a URL question-answering pipeline by chaining fetcher, converter, prompt, and LLM components with typed connections.from haystack import Pipeline from haystack.components.fetchers import LinkContentFetcher from haystack.components.converters import HTMLToDocument from haystack.components.builders import PromptBuilder from haystack.components.generators import OpenAIGenerator from haystack.utils import Secret fetcher = LinkContentFetcher() converter = HTMLToDocument() prompt_builder = PromptBuilder(template="""{% for document in documents %}{{document.content}}{% endfor %} Answer: {{query}}""") llm = OpenAIGenerator(api_key=Secret.from_env_var("OPENAI_API_KEY")) pipeline = Pipeline() pipeline.add_component("fetcher", fetcher) pipeline.add_component("converter", converter) pipeline.add_component("prompt", prompt_builder) pipeline.add_component("llm", llm) pipeline.connect("fetcher.streams", "converter.sources") pipeline.connect("converter.documents", "prompt.documents") pipeline.connect("prompt.prompt", "llm.prompt") pipeline.run({"fetcher": {"urls": ["https://haystack.deepset.ai/overview/quick-start"]}, "prompt": {"query": "How should I install Haystack?"}})Spin up a predefined chat-with-website pipeline in one line using the new template factory.from haystack import Pipeline, PredefinedPipeline pipeline = Pipeline.from_template(PredefinedPipeline.CHAT_WITH_WEBSITE) pipeline.run({"fetcher": {"urls": ["https://haystack.deepset.ai/overview/quick-start"]}, "prompt": {"query": "How should I install Haystack?"}})Create a custom embedder component with typed I/O and plug it into a retrieval pipeline.from haystack import component, Pipeline from haystack.document_stores.in_memory import InMemoryDocumentStore from haystack.components.retrievers.in_memory import InMemoryEmbeddingRetriever import random from typing import List @component class MyEmbedder: def __init__(self, dim: int = 128): self.dim = dim @component.output_types(embedding=List[float]) def run(self, text: str): return {"embedding": [random.uniform(-1.0, 1.0) for _ in range(self.dim)]} document_store = InMemoryDocumentStore() pipeline = Pipeline() pipeline.add_component("text_embedder", MyEmbedder()) pipeline.add_component("retriever", InMemoryEmbeddingRetriever(document_store=document_store)) pipeline.connect("text_embedder.embedding", "retriever.query_embedding") pipeline.run({"text_embedder": {"text": "Who lives in Berlin?"}})- ›New
haystack-aipackage replacesfarm-haystackfor Haystack 2.0; both coexist but must be installed in separate virtual environments to avoid conflicts. - ›New Pipeline class supports dynamic computation graphs with conditional control flow, loops, typed data flow, pre-run validation, and serialization; built via add_component() and connect() methods, executed with run().
- ›New
@componentdecorator and @component.output_types() decorator enable custom components with typed inputs and outputs that slot directly into pipelines. - ›New Pipeline.from_template() factory method accepts
PredefinedPipelineenum values (e.g.,PredefinedPipeline.CHAT_WITH_WEBSITE) to instantiate ready-made pipelines in one line. - ›New
PromptBuildercomponent (andDynamicPromptBuilderfor advanced cases) accepts Jinja-templated prompts where{{ }}expressions become typed pipeline inputs.
+4 moreshow less
- ›New Secret.from_env_var() utility provides type-safe secret and API-key management to prevent accidental credential leaks.
- ›Built-in components now span 20+ categories — including Generators, Embedders, Retrievers, Evaluators, Rankers, and Routers — with integrations for OpenAI, Cohere, Hugging Face, Amazon Bedrock, Google Vertex, Ollama, and many more.
- ›Document Stores provide a unified interface for vector-database backends including Weaviate, Chroma, Pinecone, Astra DB, MongoDB, Qdrant, Pgvector, Elasticsearch, OpenSearch, Neo4j, and Marqo, each paired with a dedicated retriever component.
- ›Structured logging system supports tracing correlation out of the box, with OpenTelemetry and Datadog instrumentation built in.
- ›New
- v1.25.0
Haystack v1.25.0 adds page-based document splitting, new OpenAI embedding models, and local endpoint support via
API_BASE.└──▷ GET THIS VERSION$ git clone --branch v1.25.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.25.0
└──▷ USE ITChunk a document corpus by page rather than word or sentence count — useful when downstream retrieval should respect PDF page boundaries.preprocessor = PreProcessor( split_by='page', split_overlap=0 ) docs = preprocessor.process(raw_docs)- ›Adds
split_by='page'option to the Preprocessor so documents can be chunked by page break. - ›Adds
raise_on_failureflag toBaseConverterso large batch processes can continue past individual conversion exceptions. - ›Adds support for OpenAI embedding models
text-embedding-3-largeandtext-embedding-3-small. - ›Adds
API_BASEas an optional parameter toPromptNodeandPromptModel, enabling RAG against any OpenAI-compatible local endpoint (e.g. LM Studio athttp://localhost:1234/v1). - ›Upgrades Transformers to 4.37.2, adding support for Phi-2 and Qwen2 models and improved quantization support.
- ›Adds
- v1.25.0-rc1
Haystack v1.25.0-rc1 adds page-break chunking, new OpenAI embedding models, local endpoint support, and a fault-tolerant converter flag.
└──▷ GET THIS VERSION$ git clone --branch v1.25.0-rc1 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.25.0-rc1
└──▷ USE ITChunk a document corpus by page boundaries rather than word or sentence count — useful when page layout carries semantic meaning.preprocessor = PreProcessor(split_by="page", split_length=1)
Point Haystack at a local LM Studio endpoint so your RAG pipeline runs entirely on-prem without changing any other pipeline code.prompt_model = PromptModel(model_name_or_path="gpt-3.5-turbo", api_key="ignored", model_kwargs={"API_BASE": "http://localhost:1234/v1"})Keep a bulk conversion job alive even when individual files are malformed or unreadable.converter = PDFToTextConverter(raise_on_failure=False)
- ›Adds
split_by="page"option to the preprocessor, enabling document chunking by page break. - ›Adds
raise_on_failureflag toBaseConverterso large batch processes can continue past per-document exceptions instead of halting. - ›Adds support for OpenAI embedding models
text-embedding-3-largeandtext-embedding-3-small. - ›Adds
API_BASEas an optional parameter toPromptNodeandPromptModel, enabling RAG against any OpenAI-compatible local endpoint (e.g. LM Studio athttp://localhost:1234/v1). - ›Upgrades Transformers to 4.37.2, adding support for Phi-2 and Qwen2 models and improved quantization support.
- ›Adds
- v1.24.0
Haystack v1.24.0 adds Amazon Bedrock embedding models and configurable WebDriver support for the Crawler.
└──▷ GET THIS VERSION$ git clone --branch v1.24.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.24.0
└──▷ USE ITUse a Titan embedding model hosted on Amazon Bedrock as a retriever in a Haystack pipeline.from haystack.nodes import EmbeddingRetriever retriever = EmbeddingRetriever( embedding_model="amazon.titan-embed-text-v1", document_store=document_store, aws_config={ "aws_access_key_id": "ACCESS_KEY", "aws_secret_access_key": "SECRET_KEY", "aws_session_token": "SESSION_TOKEN" } )- ›Adds
EmbeddingRetrieversupport for Amazon Bedrock embedding models, includingamazon.titan-embed-text-v1and Cohere models, via anaws_configparameter acceptingaws_access_key_id,aws_secret_access_key, andaws_session_token. - ›Adds an optional
webdriverparameter toCrawler.__init__to supply a pre-configured customWebDriverinstead of the default Chrome driver. - ›Adds
model_kwargsargument to FARMReader to support loading the model in fp16 at inference time. - ›Adds
model_kwargsargument toSentenceTransformersRankerto pass HuggingFace Transformers loading options. - ›Makes
JoinDocumentssensitive to theweightsparameter and adds score normalization whenjoin_modeisreciprocal rank fusion.
+1 moreshow less
- ›Optimizes
PineconeDocumentStore.write_documentsupserts with asynchronous requests.
- ›Adds
- v1.23.0
Haystack v1.23.0 adds Amazon Bedrock and MongoDB Atlas support, plus new converters, token splitting, and embedding instructions.
└──▷ GET THIS VERSION$ git clone --branch v1.23.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.23.0
└──▷ USE ITUse an Amazon Bedrock-hosted Llama 2 model in a PromptNode without any extra configuration beyond the model ID.from haystack.nodes import PromptNode prompt_node = PromptNode(model_name_or_path="meta.llama2-13b-chat-v1")
Connect Haystack to a MongoDB Atlas collection as a document store for indexing and retrieval.from haystack.document_stores.mongodb_atlas import MongoDBAtlasDocumentStore document_store = MongoDBAtlasDocumentStore( mongo_connection_string="mongodb+srv://USER:PASSWORD@HOST/?retryWrites=true&w=majority", database_name="my_database", collection_name="my_collection", ) document_store.write_documents(docs)- ›Adds
MongoDBAtlasDocumentStoreclass (importable fromhaystack.document_stores.mongodb_atlas) withmongo_connection_string,database_name, andcollection_nameconstructor parameters, providing MongoDB Atlas as a document store backend. - ›Adds Amazon Bedrock model support to
PromptNodeviamodel_name_or_path— pass a Bedrock model ID (e.g.meta.llama2-13b-chat-v1) to use models like Llama-2-70b-chat. - ›Adds
timeoutkeyword argument toPromptNodefor per-call timeout control over OpenAI invocations. - ›Adds
batch_sizeparameter to the__init__method ofFAISSDocumentStore, serving as the default for all methods that acceptbatch_size. - ›Adds
model_kwargsparameter toExtractiveReaderfor passing HuggingFace loading options.
+8 moreshow less
- ›Adds
split_lengthby token inPreProcessor. - ›Adds
PptxConverternode to convert.pptxfiles to Haystack Documents. - ›Adds support for dense embedding instructions used in retrieval models such as BGE and LLM-Embedder.
- ›Changes
PromptModelconstructor parameterinvocation_layer_classto also accept astr(imported at runtime), easing YAML serialization. - ›Allows defining the number of pods and pod type directly when creating a
PineconeDocumentStoreinstance. - ›Allows loading additional fields from SQUAD-format files into the
metafield of Labels. - ›Adds token limit definition for the
gpt-4-1106-previewmodel. - ›Upgrades Transformers to 4.35.2, adding support for DistilWhisper, Fuyu, Kosmos-2, SeamlessM4T, and Owl-v2 model families.
└──▷ BREAKING ON UPGRADE- !Removes deprecated
OpenAIAnswerGenerator,BaseGenerator, andGenerativeQAPipelineclasses — pipelines using these must migrate toPromptNode.
- ›Adds
- v1.22.1
Haystack v1.22.1 adds token limit support for the gpt-4-1106-preview model.
└──▷ GET THIS VERSION$ git clone --branch v1.22.1 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.22.1
- ›Adds token limit support for the
gpt-4-1106-previewmodel.
- ›Adds token limit support for the
- v1.22.0
Haystack v1.22.0 adds async Pipeline support, new Haystack 2.0 preview components, and expanded model/hardware compatibility.
└──▷ GET THIS VERSION$ git clone --branch v1.22.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.22.0
└──▷ USE ITSave a Haystack 2.0 pipeline definition to YAML for version control or reproducible deployments.with open('pipeline.yaml', 'w') as f: pipeline.dump(f)Pass a Google Custom Search engine ID through WebRetriever to scope web searches to a specific engine.retriever = WebRetriever(search_engine_kwargs={'engine': '<your-engine-id>'})- ›Adds
ByteStreamtype (withmime_typefield) for passing binary raw data across pipeline components in Haystack 2.0. - ›Adds
ChatMessagedataclass toPromptBuilderfor structured chat LLM message handling in Haystack 2.0. - ›Adds
AzureOCRDocumentConverterto convert documents via Azure's Document Intelligence Service in Haystack 2.0. - ›Adds
HTMLToDocumentcomponent to convert HTML to a Document in Haystack 2.0. - ›Adds
TransformersSimilarityRankercomponent (renamed fromSimilarityRanker) that ranks Document lists by query similarity in Haystack 2.0.
+23 moreshow less
- ›Adds
TopPSamplercomponent that selects documents using top-p (nucleus) sampling on cumulative Document scores in Haystack 2.0. - ›Adds
HuggingFaceLocalGeneratorcomponent to run Hugging Face models locally for text generation, with support for specifying stopwords in Haystack 2.0. - ›Adds
dumps,dump,loads, andloadmethods to Haystack 2.0 pipelines for saving and loading pipeline definitions in YAML format. - ›Adds
TextDocumentSplittercomponent to Haystack 2.0 for splitting long-text Documents into shorter ones matching model max-length constraints. - ›Adds
DocumentCleanercomponent to remove extra whitespace, empty lines, and headers from text Documents as a preprocessing step in Haystack 2.0. - ›Adds
TextLanguageClassifiercomponent to route an input string to different components based on detected language in Haystack 2.0. - ›Adds
FileTypeRouter(renamed from the previous router) withByteStreamhandling support for improved file routing in Haystack 2.0. - ›Adds OpenAI Document Embedder that computes embeddings using OpenAI models and stores results in each Document's
embeddingfield in Haystack 2.0. - ›Introduces
StreamingChunkdataclass for handling streamed language model output chunks with content and metadata in Haystack 2.0. - ›Adds
tokenparameter toExtractiveReaderandTransformersSimilarityRanker(replacing deprecateduse_auth_token) to allow loading private Hugging Face models in Haystack 2.0. - ›Adds
search_engine_kwargsparameter toWebRetrieverto propagate options (e.g. Google Custom Search engine ID) toWebSearch. - ›Adds
list_of_pathsargument toutils.convert_files_to_docs, enabling a list of file paths as input alongside or instead ofdir_path. - ›Adds experimental support for asynchronous Pipeline run in Haystack.
- ›Adds asyncio support to the OpenAI invocation layer and
arunmethod onPromptNodefor asynchronous execution. - ›Adds
on_final_answercallback support through Agentcallback_manager. - ›Adds Apple Silicon GPU acceleration via
mpsPyTorch backend, improving performance on M1 hardware. - ›Adds basic telemetry to Haystack 2.0 pipelines.
- ›Upgrades canals to 0.9.0, enabling variadic inputs for Joiner components and
/in connection names (e.g.text/plain). - ›Upgrades Transformers to 4.34.1, adding support for Mistral, Persimmon, BROS, ViTMatte, and Nougat models.
- ›Enables all Pinecone index types including Starter in
PineconeDocumentStore(document fetching limited to Pinecone's 10,000-vector query limit for Starter). - ›Makes
JoinDocumentsreturn only the highest-scoring document when duplicates are present. - ›Document writer now returns the count of documents written.
- ›Migrates
RemoteWhisperTranscriberto the OpenAI SDK.
└──▷ BREAKING ON UPGRADE- !The
audio,ray,onnx, andbeirextras are removed from theallextra group. - !
MemoryDocumentStoreis renamed toInMemoryDocumentStore;MemoryBM25Retrieveris renamed toInMemoryBM25Retriever;MemoryEmbeddingRetrieveris renamed toInMemoryEmbeddingRetriever. - !
SimilarityRankeris renamed toTransformersSimilarityRankerin Haystack 2.0. - !The
id_hash_keysfield is removed from the Document dataclass and fromDocumentCleaner,TextDocumentSplitter,PyPDFToDocument,AzureOCRDocumentConverter,HTMLToDocument,TextFileToDocument, andTikaDocumentConverter. - !The
arrayfield is removed from the Document dataclass. - !Document's
embeddingfield type is changed fromnumpy.ndarraytoList[float]. - !
ExtractiveReader's input is renamed fromdocumenttodocuments. - !The file-type router is renamed to
FileTypeRouterin Haystack 2.0.
- ›Adds
- v1.21.1
Haystack v1.21.1 adds async Pipeline execution and an
arunmethod on PromptNode for non-blocking LLM calls.└──▷ GET THIS VERSION$ git clone --branch v1.21.1 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.21.1
└──▷ USE ITRun a PromptNode asynchronously inside an async function to avoid blocking the event loop during LLM calls.import asyncio from haystack.nodes import PromptNode pn = PromptNode(model_name_or_path="gpt-3.5-turbo", api_key="<your-key>") async def main(): result = await pn.arun(prompt="Summarize the following text: <text>") print(result) asyncio.run(main())- ›Adds
arunmethod toPromptNodefor asynchronous execution, enabling non-blocking LLM inference in async applications. - ›Adds experimental asyncio support to the OpenAI invocation layer, allowing OpenAI-backed components to participate in async pipelines.
- ›Adds experimental support for asynchronous Pipeline run, enabling full async orchestration of pipeline components.
- ›Adds
- v1.21.0
Haystack v1.21.0 adds gpt-3.5-turbo-instruct support, a Haystack 2.0 preview install extra, and a revamped PineconeDocumentStore.
└──▷ GET THIS VERSION$ git clone --branch v1.21.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.21.0
└──▷ TRY ITTry Haystack 2.0 preview components without pulling in the full core dependency set.$ pip install farm-haystack[preview]Migrate PineconeDocumentStore queries from namespaces to the new metadata-based API after upgrading.from haystack.document_stores.pinecone import DOCUMENT_WITH_EMBEDDING # Retrieve documents that have an embedding docs_with_embedding = doc_store.get_all_documents(type_metadata=DOCUMENT_WITH_EMBEDDING) # Retrieve documents without an embedding docs_without_embedding = doc_store.get_all_documents(type_metadata="no-vector")
- ›Adds support for OpenAI's
gpt-3.5-turbo-instructmodel viaPromptNode, enabling use of OpenAI's latest instruct-tuned completion model in existing pipelines. - ›Introduces
farm-haystack[preview]installation extra to try Haystack 2.0 components and pipeline design, while also making core dependencies leaner and speeding up installation. - ›Refactors
PineconeDocumentStoreto use metadata instead of namespaces for distinguishing document types; addstype_metadataparameter to get_all_documents() and exposes theDOCUMENT_WITH_EMBEDDINGconstant fromhaystack.document_stores.pinecone. - ›Adds
AnswerBuildercomponent (Haystack 2.0 preview) that creates Answer objects from the string output of Generator components. - ›Adds
LinkContentFetchercomponent (Haystack 2.0 preview) that fetches content from a URL and converts it into a Document object for use in pipelines.
+14 moreshow less
- ›Adds
MetadataRoutercomponent (Haystack 2.0 preview) that routes documents to different pipeline edges based on the content of their metadata fields. - ›Adds PDF file support to the Haystack 2.0 Document converter via the
pypdflibrary. - ›Adds
SerperDevWebSearchcomponent (Haystack 2.0 preview) to retrieve URLs from the web using the Serper.dev API. - ›Adds
TikaDocumentConvertercomponent (Haystack 2.0 preview) to convert files of multiple types into Document objects. - ›Adds
ExtractiveReadercomponent (Haystack 2.0 preview) as a replacement for FARMReader for inference, with per-span binary classification confidence scoring. - ›Introduces GPTGenerator class (Haystack 2.0 preview) for generating completions using OpenAI Chat models such as GPT-3.5 and GPT-4.
- ›Adds GPT4Generator component (Haystack 2.0 preview) as an LLM component based on GPT35Generator.
- ›Adds
embedding_retrievalmethod toMemoryDocumentStore(Haystack 2.0 preview), exposed asMemoryEmbeddingRetriever, which retrieves relevant documents given a query embedding. - ›Renames
MemoryRetrievertoMemoryBM25Retrieverand addsMemoryEmbeddingRetriever(Haystack 2.0 preview) for embedding-based retrieval fromMemoryDocumentStore. - ›Adds OpenAI Text Embedder component (Haystack 2.0 preview) that uses OpenAI models to embed strings into vectors.
- ›Adds
PromptBuildercomponent (Haystack 2.0 preview) to render prompts from template strings. - ›Adds
prefixandsuffixattributes toSentenceTransformersDocumentEmbedder(Haystack 2.0 preview) for prepending/appending text to documents before embedding, enabling full use of models such as E5. - ›Adds support for date values in document store filters (Haystack 2.0 preview).
- ›Adds
UrlCacheCheckercomponent (Haystack 2.0 preview) that checks whether documents from given URLs are already present in the store, returning cached documents and unmatched URLs on a separate connection.
└──▷ BREAKING ON UPGRADE- !
SklearnQueryClassifieris removed; users must migrate toTransformersQueryClassifier. - !
PineconeDocumentStorenow uses metadata instead of namespaces to distinguish document types — thenamespaceparameter to get_all_documents() no longer works; callers must switch to thetype_metadataparameter (e.g.type_metadata=DOCUMENT_WITH_EMBEDDINGortype_metadata='no-vector').
- ›Adds support for OpenAI's
- v1.20.0
Haystack v1.20.0 adds LostInTheMiddleRanker, DiversityRanker, allowed_domains for WebRetriever, and dynamic filter support in custom OpenSearch/Elasticsearch queries.
└──▷ GET THIS VERSION$ git clone --branch v1.20.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.20.0
└──▷ USE ITBuild a RAG pipeline that diversifies retrieved documents and then reorders them with the Lost-in-the-Middle strategy before generation.from haystack.nodes import WebRetriever, TopPSampler, DiversityRanker, LostInTheMiddleRanker from haystack.pipelines import Pipeline web_retriever = WebRetriever(api_key=search_key, top_search_results=5, mode="preprocessed_documents", top_k=50) sampler = TopPSampler(top_p=0.97) diversity_ranker = DiversityRanker() litm_ranker = LostInTheMiddleRanker(word_count_threshold=1024) pipeline = Pipeline() pipeline.add_node(component=web_retriever, name="Retriever", inputs=["Query"]) pipeline.add_node(component=sampler, name="Sampler", inputs=["Retriever"]) pipeline.add_node(component=diversity_ranker, name="DiversityRanker", inputs=["Sampler"]) pipeline.add_node(component=litm_ranker, name="LostInTheMiddleRanker", inputs=["DiversityRanker"]) pipeline.add_node(component=prompt_node, name="PromptNode", inputs=["LostInTheMiddleRanker"])
Pass dynamic filters at query-time to a BM25Retriever using the new${filters}placeholder in a custom OpenSearch query, without modifying the stored query template.retriever = BM25Retriever( custom_query=""" { "query": { "bool": { "should": [{"multi_match": { "query": ${query}, "type": "most_fields", "fields": ["content", "title"]}}], "filter": ${filters} } } }""" ) retriever.retrieve( query="What is the meaning of life?", filters={"year": [2019, 2020], "quarter": [1, 2, 3], "date": {"$gte": "2019-03-01"}} )Scope a WebRetriever to specific domains to build a 'talk to your docs' pipeline without off-site noise.web_retriever = WebRetriever( api_key=search_key, allowed_domains=["docs.haystack.deepset.ai", "haystack.deepset.ai"], top_search_results=10, mode="preprocessed_documents" )- ›Adds
LostInTheMiddleRankerclass, which reorders documents so the most relevant appear at the beginning and end of the context window, implementing the 'Lost in the Middle' strategy for RAG pipelines; accepts aword_count_thresholdparameter. - ›Adds
DiversityRankerclass, which uses sentence-transformer models to rank documents so each successive result is maximally semantically dissimilar from already-selected ones; accepts atop_kparameter. - ›Adds
${filters}placeholder support incustom_queryfor BM25Retriever withOpenSearchand Elasticsearch, enabling dynamic query-time filters without modifying the stored query template. - ›Adds
allowed_domainsparameter toWebRetriever, enabling domain-scoped searches for 'talk to a website' and 'talk to docs' use cases. - ›Adds
search_fieldsparameter toDeepsetCloudDocumentStoresparse queries, allowing BM25Retriever to search meta fields such astitlealongside documentcontent.
+11 moreshow less
- ›Adds
FileExtensionClassifierto Haystack 2.0 preview components. - ›Adds
SentenceTransformersDocumentEmbedderto Haystack 2.0 preview, storing computed embeddings in theembeddingfield of each Document. - ›Adds
SentenceTransformersTextEmbedderto Haystack 2.0 preview for embedding arbitrary strings into vectors. - ›Adds Answer base class,
GeneratedAnswer, andExtractedAnswertypes for Haystack v2. - ›Enhances
FileTypeClassifierto detect media file types includingmp3,mp4,mpeg, andm4a. - ›Adds PDF support and custom User-Agent header to
LinkContentFetcher, plus a mechanism to register new content handlers dynamically. - ›Enables setting
max_lengthwhen runningPromptNodewith local Hugging Facetext2text-generationmodels. - ›Enables passing
trust_remote_code=Trueto load tokenizers for prompt models not natively supported by Transformers. - ›Allows
WebRetrieverusers to supply a customLinkContentFetcherinstance. - ›Refactors
DocumentWriterto accept a genericDocumentStoreinstead of usingDocumentStoreAwareMixin. - ›Refactors
MemoryRetrieverto require aMemoryDocumentStoredirectly instead of usingDocumentStoreAwareMixin.
└──▷ BREAKING ON UPGRADE- !The OpenSearch
custom_queryold per-field filter placeholders (e.g.${years},${quarters},${date}) are no longer supported; replace all filter expressions with the single${filters}placeholder. - !Custom
PromptModelInvocationLayersubclasses: invoke() no longer receives prompt template parameters (such asquery,documents) as keyword arguments; existing custom layers must be updated accordingly.
- ›Adds
- v1.19.0
Haystack v1.19 adds Elasticsearch 8 support, a RecentnessRanker, Anthropic Claude 2, and Llama 2 on SageMaker.
└──▷ GET THIS VERSION$ git clone --branch v1.19.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.19.0
└──▷ TRY ITUse the new Elasticsearch 8 backend by installing the dedicated extra — auto-detection picks the right Document Store at import time.$ pip install farm-haystack[elasticsearch8]Run Llama 2 chat hosted on AWS SageMaker through PromptNode by supplying the endpoint name, AWS profile, and EULA acceptance attribute.from haystack.nodes import PromptNode prompt_node = PromptNode( model_name_or_path="sagemaker-llama-2-chat-endpoint-name", model_kwargs={ "aws_profile_name": "my_aws_profile_name", "aws_custom_attributes": {"accept_eula": True} } ) chat = [[{"role": "user", "content": "Summarize CVE mitigations for Log4Shell."}]] print(prompt_node(chat))- ›Adds
farm-haystack[elasticsearch8]install extra andElasticsearchDocumentStoreauto-detection that selects the correct backend based on the installed Elasticsearch client version (covers ES 8 and ES <=7.5). - ›Adds
farm-haystack[elasticsearch7]install extra alongside the newelasticsearch8extra for explicit version pinning. - ›Introduces
RecentnessRankerinhaystack.nodeswithdate_meta_field,ranking_mode, andweightparameters to blend document age with relevance scores. - ›Adds
embed_meta_fieldssupport to Ranker nodes, enabling metadata to be included in the text used for ranking. - ›Adds support for list-typed
embed_meta_fieldswhen embedding metadata fields in retrievers.
+9 moreshow less
- ›Extends Anthropic Claude support to Claude 2 models with updated context window sizes and a new streaming API via
PromptNode. - ›Enables Llama 2 (including chat variant) on AWS SageMaker via
PromptNodeusingaws_profile_nameandaws_custom_attributesinmodel_kwargs. - ›Upgrades dependency to
transformersv4.31.0, enabling Llama 2 support for local inference. - ›Adds global progress bar suppression capability to pipelines.
- ›Adds
OpenAI-Organizationheader support for OpenAI authentication. - ›Introduces
LinkContentFetchernode by extracting link-retrieval logic fromWebRetrieverinto a standalone component. - ›Adds BM25 retrieval support for
MemoryDocumentStore. - ›Adds batch mode for
MemoryRetriever(v2). - ›Introduces a Store protocol (v2) and extends
pipeline.add_componentto support stores.
- ›Adds
- v1.18.0
Haystack v1.18 adds AWS SageMaker LLM support, PromptHub integration, ConversationalAgent tools, and a new CohereRanker node.
└──▷ GET THIS VERSION$ git clone --branch v1.18.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.18.0
└──▷ USE ITPull a ready-made prompt from PromptHub by name to classify topics without writing a prompt from scratch.import os from haystack.nodes import PromptNode, PromptTemplate template = PromptTemplate("deepset/topic-classification") prompt_node = PromptNode( model_name_or_path="text-davinci-003", api_key=os.environ.get("OPENAI_API_KEY") ) result = prompt_node.prompt( prompt_template=template, documents="YOUR_DOCUMENTS", options=["sports", "politics", "technology"] )Equip a ConversationalAgent with a QA pipeline tool so it can answer domain-specific questions mid-conversation.from haystack.agents import Tool from haystack.agents.conversational import ConversationalAgent search_tool = Tool( name="USA_Presidents_QA", pipeline_or_node=presidents_qa_pipeline, description="useful for when you need to answer questions about US presidents." ) agent = ConversationalAgent(prompt_node=prompt_node, tools=[search_tool]) agent.run("Who was the 35th president of the United States?")- ›Adds AWS SageMaker-hosted LLM support to
PromptNodeviamodel_kwargskeysaws_profile_nameandaws_region_name, enabling open-source models deployed on SageMaker endpoints. - ›Introduces
PromptHubintegration:PromptTemplatenow accepts a hub prompt name (e.g.'deepset/topic-classification') directly, with local caching of fetched prompts. - ›Adds
toolsparameter toConversationalAgentfor attaching Tool instances (pipelines or nodes) to a chat agent. - ›Adds
prompt_templateparameter toConversationalAgent.__init__for customising the agent's prompt at construction time. - ›Adds
CohereRankernode backed by the Cohere reranking endpoint.
+8 moreshow less
- ›Adds
batch_sizeparameter toWeaviateDocumentStorequery methods. - ›Adds batching support for querying in
ElasticsearchDocumentStoreandOpenSearchDocumentStore. - ›Adds
current_datetimeshaper function for use in pipeline prompt construction. - ›Adds
max_chars_checkhard document length limit to pipeline processing. - ›Adds optional content moderation for
OpenAIPromptNodeandOpenAIAnswerGenerator. - ›Supports passing model parameters to
HFLocalInvocationLayerviamodel_kwargsfor direct model usage. - ›Supports setting a custom
api_basefor OpenAI nodes. - ›New
farm-haystack[inference]extra installs PyTorch and related dependencies for local model execution, keeping the base install lighter for API-only users.
└──▷ BREAKING ON UPGRADE- !
PromptTemplateno longer acceptsnameorprompt_textparameters; usepromptandoutput_parserinstead. - !
Seq2SeqGeneratorand RAGenerator have been removed; usePromptNodeinstead. - !The deprecated
PDFToTextOCRConverternode has been removed. - !The deprecated
return_table_cellparameter has been removed. - !PyTorch and inference-related dependencies are no longer installed by default; run
pip install farm-haystack[inference]to restore local model support. - !Weaviate authentication has been simplified (
feat!: simplify weaviate auth); existing auth configuration may need to be updated.
- ›Adds AWS SageMaker-hosted LLM support to
- v1.17.0
Haystack v1.17 adds ConversationalAgent with memory, Anthropic and Cohere LLM support, Weaviate auth, and streaming for HF Inference Endpoints.
└──▷ GET THIS VERSION$ git clone --branch v1.17.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.17.0
└──▷ USE ITBuild a chat application with summarized memory to stay within token limits.from haystack.agents.memory import ConversationalSummaryMemory from haystack.agents import ConversationalAgent summary_memory = ConversationalSummaryMemory(prompt_node=prompt_node) agent = ConversationalAgent(prompt_node=prompt_node, memory=summary_memory) response = agent.run(user_input="What are the main causes of climate change?")
Override generation parameters per pipeline run without changing the PromptNode definition.pipeline.run( query="Summarize this document", params={ "PromptNode": { "generation_kwargs": {"max_new_tokens": 200, "temperature": 0.7} } } )- ›Adds
ConversationalAgentclass for building chat applications, accepting aPromptNodeand an optionalmemoryargument for conversation history injection. - ›Adds
ConversationSummaryMemory(also referenced asConversationalSummaryMemory) to condense chat history before injecting into the prompt, keeping usage within model token limits. - ›Adds
AnthropicInvocationLayerto supportclaudemodels from Anthropic as aPromptNodebackend. - ›Adds
CohereInvocationLayerto supportcommandmodels from Cohere as aPromptNodebackend. - ›Adds
AuthBearerTokenandAuthClientCredentialsauthentication options toWeaviateDocumentStore.
+7 moreshow less
- ›Adds
max_tokensparameter toBaseGeneratorparams, exposing token-limit control across generator implementations. - ›Adds streaming support to
HFInferenceEndpointInvocationLayerfor token-by-token output from Hugging Face Inference Endpoints. - ›Adds streaming support to the HF local runtime invocation layer.
- ›Enables passing
generation_kwargstoPromptNodeat pipeline.run() time, allowing per-run overrides of generation parameters. - ›Adds BLIP model support to
TransformersImageToTextcomponent. - ›Adds Google API as a search engine provider option.
- ›Introduces
generalimportto defer missing-dependency errors from import time to actual usage time, reducing mandatory dependencies for a basepip install farm-haystack.
└──▷ BREAKING ON UPGRADE- !
MilvusDocumentStoreis removed from core Haystack; it must now be installed separately from thehaystack-extrasrepo. - !
BaseKnowledgeGraphis removed from the library. - !The
PDFToTextOCRConverternode is removed. - !Schema objects'
to_dict,from_dict,to_json, andfrom_jsonmethods have been updated to handle Dataframes, which may change serialization behavior for existing code.
- ›Adds
- v1.16.0
Haystack v1.16 adds GPT-4 and AzureChatGPT support, streaming, a Haystack CLI, and more flexible document routing.
└──▷ GET THIS VERSION$ git clone --branch v1.16.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.16.0
└──▷ USE ITUse GPT-4 in a multi-turn chat pipeline — drop-in for existing ChatGPT workflows with higher capability.from haystack.nodes import PromptModel, PromptNode prompt_model = PromptModel("gpt-4", api_key=api_key) prompt_node = PromptNode(prompt_model) messages = [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Summarize the attached document."}, ] result = prompt_node(messages)- ›Adds PromptModel('gpt-4', api_key=...) support inside
PromptNodeand Agent, enabling chat-style multi-turn conversations with GPT-4. - ›Adds
AzureChatGPTinvocation layer forPromptNode, enabling Azure-hosted ChatGPT endpoints via the new invocation layer style. - ›Adds ChatGPT streaming support via
PromptNodefor real-time token-by-token output. - ›Adds a Hugging Face Inference API invocation layer for
PromptNode, enabling remote HF-hosted model inference without local GPU. - ›Adds
MemoryDocumentStorefor the new Pipelines API.
+6 moreshow less
- ›Adds arbitrary
crawler_depthparameter to the Crawler class, allowing configurable recursive web crawling depth. - ›Enhances
RouteDocumentsnode to emit an extra route for unmatched Documents and addsList[List[str]]support formetadata_values, preventing silent document loss on missing metadata fields. - ›Adds filtering support for Weaviate when used for BM25 querying.
- ›Adds a Haystack CLI (
haystack) for command-line management. - ›Adds a
load documents from remotehelper function for fetching documents from remote sources. - ›Deprecates RAGenerator and
Seq2SeqGenerator; both will be removed in v1.18 —PromptNodeis the recommended replacement.
└──▷ BREAKING ON UPGRADE- !Python 3.7 is no longer supported; upgrade to Python 3.8 or later.
- !
PreProcessornow requiresfarm-haystack[preprocessing]; installing the base package no longer pulls it in. - !
DocxToTextConverter,TikaConverter, andLangdetectDocumentLanguageClassifiernow requirefarm-haystack[file-conversion]. - !
ElasticsearchDocumentStorenow requiresfarm-haystack[elasticsearch]. - !
TableCellreplaces Span for indicating table cell coordinates. - !Default
save_dirfor FARMReader.train() changed tof'./saved_models/{self.inferencer.model.language_model.name}'. - !Using
PreProcessorwithsplit_respect_sentence_boundary=Truemay return a different set of Documents than in v1.15.
- ›Adds PromptModel('gpt-4', api_key=...) support inside
- v1.15.0
Haystack v1.15.0 adds LLM Agents with Tools, ChatGPT support via
gpt-3.5-turbo,AnswerParser,JsonConverter, Whisper node, and Azure OpenAI embeddings.└──▷ GET THIS VERSION$ git clone --branch v1.15.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.15.0
└──▷ USE ITBuild a multi-hop web QA agent that loops over a search tool to answer complex questions.web_qa_tool = Tool( name="Search", pipeline_or_node=WebQAPipeline(retriever=web_retriever, prompt_node=web_qa_pn), description="useful for when you need to Google questions.", output_variable="results", ) agent = Agent( prompt_node=agent_pn, prompt_template=prompt_template, tools=[web_qa_tool], final_answer_pattern=r"Final Answer\s*:\s*(.*)", ) agent.run(query="What is the capital of the country that won the 2022 FIFA World Cup?")Parse LLM answers directly into Haystack Answer objects usingAnswerParserinside aPromptTemplate.PromptTemplate( name="question-answering", prompt_text="Given the context please answer the question.\nContext: {join(documents)}\nQuestion: {query}\nAnswer: ", output_parser=AnswerParser(), )Chat with ChatGPT in a multi-turn conversation usingPromptModelwithgpt-3.5-turbo.prompt_model = PromptModel("gpt-3.5-turbo", api_key=api_key) prompt_node = PromptNode(prompt_model) messages = [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Who won the world series in 2020?"}, {"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."}, {"role": "user", "content": "Where was it played?"}, ] result = prompt_node(messages)- ›Adds Agent class and Tool wrapper, enabling LLM-driven agents that dynamically plan and execute multi-step actions using a list of Tool objects and a
PromptNode; configured viaprompt_node,prompt_template,tools, andfinal_answer_patternarguments, and invoked with agent.run(query=...). - ›Adds
output_parserparameter toPromptTemplate, with a built-inAnswerParserthat converts raw LLM output into Haystack Answer, Document, or Label objects. - ›Adds function-call syntax inside
prompt_text(e.g.,{join(documents)}) toPromptTemplate, enabling in-template transformations of input documents. - ›Adds
top_kparameter toPromptNodefor controlling the number of outputs returned. - ›Adds
JsonConverternode for converting pipeline outputs to JSON format.
+7 moreshow less
- ›Adds Whisper node for audio transcription within Haystack pipelines.
- ›Adds Azure OpenAI embeddings support, enabling Azure as an OpenAI-compatible endpoint for embedding and prompt operations.
- ›Adds support for ChatGPT (
gpt-3.5-turbo) throughPromptModel, including multi-turn chat via a message list withroleandcontentfields. - ›Adds automatic OCR detection mechanism to PDF converters, improving performance by only invoking OCR when needed.
- ›Adds execution time reporting for pipeline components in
_debugoutput. - ›Exposes prompt text to Answer and
EvaluationResultobjects for traceability. - ›Extracts
AnswerToSpeechandDocumentToSpeechinto the separatehaystack-extrasrepo, installable viapip install farm-haystack-text2speech.
└──▷ BREAKING ON UPGRADE- !
OpenDistroElasticsearchDocumentStorehas been removed; any code referencing it will break on upgrade. - !
AnswerToSpeechandDocumentToSpeechnodes have been removed from the main package; installfarm-haystack-text2speechfrom thehaystack-extrasrepo to continue using them. - !
ElasticsearchRetrieverandElasticsearchFilterOnlyRetrieverhave been removed. - !The
id_hash_keysparameter has been removed from thefrom_dictmethod. - !The REST API Dockerfile now uses
uvicorninstead ofgunicornas the server; deployments that relied ongunicorn-specificbehavior or config will need updating. - !Crawler standardization changes increase conformance with Pipeline conventions but may break existing Crawler configurations.
- !
PDFToTextConvertermultiprocessing changes simplify installation but alter prior behavior; existing setups should be tested.
- ›Adds Agent class and Tool wrapper, enabling LLM-driven agents that dynamically plan and execute multi-step actions using a list of Tool objects and a
- v1.14.0
Haystack v1.14.0 adds Shaper, PromptNode run_batch/model_kwargs/top_k, IVF+PQ for OpenSearch, JsonConverter, and more.
└──▷ GET THIS VERSION$ git clone --branch v1.14.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.14.0
└──▷ USE ITPass model-specific generation parameters to PromptNode at initialisation, such as temperature and stop sequences.from haystack.nodes import PromptNode node = PromptNode('gpt-3.5-turbo', model_kwargs={'temperature': 0.2, 'stop': ['\n']})Initialise OpenSearchDocumentStore with IVF+Product Quantization so the index is trained automatically on first use.from haystack.document_stores import OpenSearchDocumentStore store = OpenSearchDocumentStore( index='my_index', embedding_field='embedding', embedding_dim=768, ivf_train_size=10000 )- ›Adds Shaper node to transform and reshape data between pipeline components, usable independently or as a
PromptNodehelper. - ›Adds
run_batchmethod toPromptNodefor batch inference. - ›Adds
model_kwargsoption toPromptNodefor passing arbitrary model parameters. - ›Adds
top_kparameter toPromptNode. - ›Exposes
output_variableinPromptNoderesult.
+15 moreshow less
- ›Adds
train_indexmethod andivf_train_sizeinitialisation parameter toOpenSearchDocumentStorefor IVF and IVF with Product Quantization index training. - ›Adds
JsonConverternode for converting JSON inputs in pipelines. - ›Adds frontmatter-to-meta extraction in
MarkdownConverter. - ›Adds page range support to PDF converters.
- ›Adds
use_prefilteringparameter toDeepsetCloudDocumentStore. - ›Adds BM25 support for tables in
InMemoryDocumentStore. - ›Adds support for custom headers in document stores.
- ›Adds support for multiple
RayPipelineinstances running concurrently. - ›Allows all training options for
SentenceTransformersEmbeddingRetriever. - ›Adds user-configurable timeout for remote APIs.
- ›Enables secure model loading by default.
- ›Adds
OpenAIErrorto the retry mechanism. - ›Warns users when
max_tokensis too short for OpenAI models. - ›Includes testing facilities in the
haystackpackage for downstream consumers. - ›Supports multiple
document_idsin the Answer object for generative QA.
└──▷ BREAKING ON UPGRADE- !The REST API schema for tables has been updated to be consistent with
Document.to_dict; existing table schema integrations may require adjustment. - !The Answer object now supports multiple
document_ids(previously a single value); code that assumes a singledocument_idfield will need to be updated. - !Defaults for
OpenAIAnswerGeneratorhave changed; existing pipelines relying on previous defaults may behave differently after upgrade.
- ›Adds Shaper node to transform and reshape data between pipeline components, usable independently or as a
- v1.13.2
Haystack v1.13.2 adds
use_prefilteringparameter to DeepsetCloudDocumentStore└──▷ GET THIS VERSION$ git clone --branch v1.13.2 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.13.2
- ›Adds
use_prefilteringparameter toDeepsetCloudDocumentStoreto control whether pre-filtering is applied during document retrieval.
- ›Adds
- v1.13.1
Haystack v1.13.1 adds the Shaper component and frontmatter-to-meta extraction in
MarkdownConverter.└──▷ GET THIS VERSION$ git clone --branch v1.13.1 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.13.1
- ›Adds Shaper component for reshaping and transforming data between pipeline nodes.
- ›Adds frontmatter extraction to meta in
MarkdownConverter, surfacing YAML/TOML front matter as structured document metadata.
- v1.13.0
Haystack v1.13 adds stop words for PromptNode, ImageToText and CsvTextConverter nodes, tiktoken support, and HA for Weaviate.
└──▷ GET THIS VERSION$ git clone --branch v1.13.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.13.0
└──▷ USE ITStop LLM output at a sentinel phrase to keep answers concise when using PromptNode.from haystack.nodes import PromptNode pn = PromptNode( model_name_or_path='text-davinci-003', stop_words=['\nHuman:', 'END'] ) result = pn.run(prompt='Summarize the following document: ...')- ›Adds
stop_wordslist parameter toPromptNodeto halt LLM text generation when any stop word is encountered; stop words are excluded from the response. - ›Adds
indexparameter toTfidfRetrieverto specify which index to query. - ›Adds
knn_engineparameter toSearchEngineDocumentStoreto makescore_scripta first-class citizen for KNN search. - ›New
ImageToTextnode generates captions from image files and produces Haystack Document objects from them. - ›New
CsvTextConverternode loads CSV files of FAQ question-answer pairs and sends them to aDocumentStorefor FAQ matching pipelines.
+10 moreshow less
- ›Adds retry with exponential back-off to
PromptNode's OpenAI model integrations. - ›Supports
cl100k_basetokenization via OpenAI'stiktokenlibrary for dramatically faster tokenization of GPT models; falls back to HuggingFace tokenizers on unsupported platforms (Python < 3.8, arm64, macOS). - ›Adds high-availability (HA) support for the Weaviate
DocumentStore. - ›Enables
text-embedding-ada-002model forEmbeddingRetriever. - ›Updates Cohere embedding models support and adds use of Cohere's
truncateoption inCohere.embed. - ›Stores
id_hash_keysin Document objects to make documents clonable. - ›Adds async functionality support for Ray Serve pipelines.
- ›Makes new sklearn models the default in
QueryClassifier. - ›Adds
PromptModel,PromptNode, andPromptTemplateto expand LLM support. - ›Raises a warning in Preprocessor when a document's length exceeds the configured threshold.
└──▷ BREAKING ON UPGRADE- !Native PyTorch AMP replaces the previous AMP integration; existing code relying on the old AMP behaviour will break.
- !
invocation_contextis moved frommetato its own pipeline variable; code readingmeta['invocation_context']will break. - !The
batch_sizeparameter names in distillation are renamed for consistency; existing calls using the old names will break.
- ›Adds
- v1.12.1
Haystack v1.12.1 adds PromptNode for LLM integration, BM25 support in InMemoryDocumentStore, and parallel dense batch search for Elasticsearch/OpenSearch.
└──▷ GET THIS VERSION$ git clone --branch v1.12.1 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.12.1
└──▷ USE ITUse the latest OpenAI or Cohere embedding models in EmbeddingRetriever to get higher-quality dense retrieval without changing any other pipeline code.from haystack.nodes import EmbeddingRetriever # OpenAI retriever = EmbeddingRetriever( embedding_model="text-embedding-ada-002", batch_size=32, api_key=api_key, max_seq_len=8191 ) # Cohere multilingual retriever = EmbeddingRetriever( embedding_model="multilingual-22-12", batch_size=16, api_key=api_key )- ›Introduces
PromptNode(inhaystack.nodes.prompt) withPromptModelandPromptTemplate, enabling LLM-powered NLP tasks via prompt templates; supports Google Flan-T5 and OpenAI GPT-3 models (e.g.google/flan-t5-base,text-davinci-003) standalone or chained in pipelines. - ›Adds
all_terms_must_matchparameter to BM25Retriever, configurable at runtime. - ›Adds
query_by_embedding_batchtoElasticsearchDocumentStoreandOpenSearchDocumentStore, enabling parallel dense searches viamsearch— up to 49% faster forrun_batch,eval_batch, andMostSimilarDocumentsPipeline. - ›Extends
EmbeddingRetrieverto support Cohere multilingual embedding models (e.g.multilingual-22-12) and OpenAI embedding models (e.g.text-embedding-ada-002withmax_seq_len=8191). - ›Adds BM25Retriever support to
InMemoryDocumentStore, making it the first dependency-free document store to support all Haystack retrievers.
+2 moreshow less
- ›Adds
offsets_in_contextfield to evaluation results. - ›Enables
SQLDocumentStoreto store metadata using JSON.
└──▷ BREAKING ON UPGRADE- !Docker images
deepset/haystack-cpu,deepset/haystack-gpu, and their tags are discontinued; Dockerfiles/Dockerfile,/Dockerfile-GPU, and/Dockerfile-GPU-minimalwill be removed from the codebase after this release.
- ›Introduces
- v1.11.0
Haystack v1.11.0 adds CohereEmbeddingEncoder, headline extraction from Markdown/PDF, TextIndexingPipeline, and document_store parameter on all retrievers.
└──▷ GET THIS VERSION$ git clone --branch v1.11.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.11.0
└──▷ USE ITExtract structured headlines (with position and level) from a Markdown file when indexing, so downstream components can use document structure.from haystack.nodes import MarkdownConverter converter = MarkdownConverter(extract_headlines=True) docs = converter.convert(file_path="report.md", meta=None) print(docs[0].meta['headlines']) # [{'headline': 'Introduction', 'start_idx': 0, 'level': 1}, ...]Pass a specific document store at query time instead of at retriever construction, enabling a single retriever instance across multiple stores.results = retriever.retrieve( query="What is the capital of France?", document_store=alternate_document_store )- ›Adds
CohereEmbeddingEncodertoEmbeddingRetriever, supporting Cohere modelssmall,medium, andlargefor document and query embeddings via API key. - ›Adds
extract_headlinesparameter toMarkdownConverterandParsrConverter; extracted headlines are stored indocument.meta['headlines']as a list of dicts withheadline,start_idx, andlevelfields. - ›Adds
document_storeparameter to all BaseRetriever.retrieve() and BaseRetriever.retrieve_batch() implementations, allowing the document store to be specified at query time. - ›Introduces
TextIndexingPipelinefor straightforward text indexing workflows. - ›Adds
__contains__method to Span for membership testing.
+2 moreshow less
- ›Adds exponential backoff decorator applied to OpenAI requests to handle rate limiting automatically.
- ›Adds indexing pipeline type support.
└──▷ BREAKING ON UPGRADE- !
Milvus1DocumentStoreis removed; Milvus versions below 2.x are no longer supported.Milvus2DocumentStorehas been renamed toMilvusDocumentStore— code referencing either old name will break. - !A duplicated meta
namefield that was previously added to document content before embedding in theupdate_embeddingsworkflow has been removed; embeddings generated before this change may differ.
- ›Adds
- v1.10.0
Haystack v1.10 adds OpenAI embeddings, multimodal retrieval, HNSW/OpenSearch support, and multi-platform Docker images.
└──▷ GET THIS VERSION$ git clone --branch v1.10.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.10.0
└──▷ USE ITPerform text-to-image retrieval using a multimodal CLIP model across an image document store.retriever = MultiModalRetriever( document_store=InMemoryDocumentStore(embedding_dim=512), query_embedding_model="sentence-transformers/clip-ViT-B-32", query_type="text", document_embedding_models={"image": "sentence-transformers/clip-ViT-B-32"} )- ›Adds
OpenAIEmbeddingEncodertoEmbeddingRetriever, enabling document and query embeddings via OpenAI modelsada,babbage,davinci, orcurieusing an API key. - ›Adds
MultiModalRetrieversupporting independent modalities for query and documents — enabling text-to-image, text-to-table, text-to-text, image similarity, and table similarity retrieval via configurablequery_embedding_model,query_type, anddocument_embedding_modelsparameters. - ›Adds
filtersparameter to MostSimilarDocumentsPipeline.run() and run_batch() for filtered similarity searches. - ›Adds HNSW support for cosine similarity in FAISS-backed OpenSearch (
FAISSDocumentStorewith OpenSearch). - ›Adds support for Elasticsearch 7.16.2 in
ElasticSearchDocumentStore.
+3 moreshow less
- ›Adds exponential backoff decorator applied to OpenAI requests to handle rate limiting.
- ›Updates
EntityExtractorto handle long texts with improved postprocessing. - ›Publishes
deepset/haystackDocker images for bothlinux/amd64andlinux/arm64platforms.
└──▷ BREAKING ON UPGRADE- !The
textargument in theembed_queriesmethod forDensePassageRetrieverandEmbeddingRetrieveris renamed toqueries; callers using the keyword argumenttext=will break.
- ›Adds
- v1.9.0
Haystack v1.9.0 adds a health-check endpoint, layout-based PDF extraction, MultipleNegativesRankingLoss for retriever training, and a unified Docker image.
└──▷ GET THIS VERSION$ git clone --branch v1.9.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.9.0
└──▷ USE ITTrain an EmbeddingRetriever with MultipleNegativesRankingLoss for better contrastive learning on in-batch negatives.retriever.train( data_dir="training_data/", train_filename="train.json", loss_function="MultipleNegativesRankingLoss" )- ›Adds a health check endpoint to the REST API, enabling liveness probes and load-balancer integration.
- ›Adds
MultipleNegativesRankingLossas a training loss option forEmbeddingRetrieverwhen using sentence-transformers. - ›Adds public layout-based text extraction support to
PDFToTextConverter, enabling structure-aware PDF parsing. - ›Adds exponential backoff with exponentially decreasing batch size for OpenSearch and Elasticsearch clients under load.
- ›Publishes a new unified
deepset/haystackDocker image with support for multiple flavors and versions via Docker tags.
+3 moreshow less
- ›Standardizes the
devicesparameter and device initialization across pipeline components. - ›Adds
PineconeDocumentStorewarnings when indexing metadata would cause filters to return no documents. - ›Updates
languageparameter documentation and types forPreProcessor, clarifying supported language values.
└──▷ BREAKING ON UPGRADE- !Pre-Haystack-1.0 import paths are removed and no longer supported.
- v1.8.0
Haystack v1.8.0 adds batch pipeline eval, early stopping for training, SQL-free PineconeDocumentStore, and FAISS support in OpenSearch.
└──▷ GET THIS VERSION$ git clone --branch v1.8.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.8.0
└──▷ USE ITStop reader training automatically when loss improvement drops below a threshold, saving GPU time on large training runs.from haystack.nodes import FARMReader from haystack.utils.early_stopping import EarlyStopping reader = FARMReader(model_name_or_path="deepset/roberta-base-squad2-distilled") reader.train( data_dir="data/squad20", train_filename="dev-v2.0.json", early_stopping=EarlyStopping(min_delta=0.001), use_gpu=True, n_epochs=8, save_dir="my_model" )Use FAISS as the k-NN engine in OpenSearchDocumentStore for faster approximate nearest-neighbour search.from haystack.document_stores import OpenSearchDocumentStore document_store = OpenSearchDocumentStore(knn_engine="faiss")
- ›Adds pipeline.eval_batch() method to
ExtractiveQAPipelinefor GPU-accelerated batch evaluation over large datasets, reducing evaluation run time. - ›Adds
EarlyStoppingclass (importable fromhaystack.utils.early_stopping) withmin_deltaparameter for FARMReader.train() andDensePassageRetrievertraining; monitorsloss,EM,f1,top_n_accuracy(FARMReader) orloss,acc,f1,average_rank(DensePassageRetriever). - ›Adds
knn_engineparameter toOpenSearchDocumentStoreto select betweennmslibandfaissapproximate k-NN libraries; falls back to exact vector calculation if the loaded index was built with a different engine. - ›
PineconeDocumentStoreno longer requires a local SQL database — initialization now only needs a Pinecone API key. - ›Adds exact list matching support for field filters in
ElasticsearchDocumentStore.
+1 moreshow less
- ›Adds progress bar to upload_files() in the deepset Cloud client.
- ›Adds pipeline.eval_batch() method to
- v1.7.1
Haystack v1.7.1 lets you specify a configurable list of models to cache instead of a single hardcoded one.
└──▷ GET THIS VERSION$ git clone --branch v1.7.1 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.7.1
- ›Supports passing a configurable list of models to cache, replacing the previously hardcoded single-model approach.
- v1.7.0
Haystack v1.7 adds OpenAI GPT-3 generation, zero-shot query classification, page-number metadata, gradient accumulation, and expanded Ray Serve support.
└──▷ GET THIS VERSION$ git clone --branch v1.7.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.7.0
└──▷ USE ITRoute queries to different pipeline branches based on semantic topic using zero-shot classification — no labelled training data required.from haystack.nodes import TransformersQueryClassifier classifier = TransformersQueryClassifier( model_name_or_path="typeform/distilbert-base-uncased-mnli", use_gpu=True, task="zero-shot-classification", labels=["music", "cinema", "food"], ) result = classifier.run(query="Who directed Pulp Fiction?") print(result)Control Ray Serve replica count and resource allocation per node directly in a Pipeline YAML for production Ray deployments.pipelines: - name: ray_query_pipeline nodes: - name: EmbeddingRetriever replicas: 2 inputs: [ Query ] serve_deployment_kwargs: num_replicas: 2 version: Twenty ray_actor_options: num_gpus: 0.25 num_cpus: 0.5 max_concurrent_queries: 17 - name: Reader inputs: [ EmbeddingRetriever ]- ›Adds
OpenAIAnswerGeneratornode withapi_key,max_tokens, andtemperatureparameters for GPT-3-powered generative QA. - ›Adds
task='zero-shot-classification'andlabelsparameters toTransformersQueryClassifier, enabling multi-class zero-shot query routing with any MNLI-style model. - ›Adds
add_page_number=Trueparameter toParsrConverter,AzureConverter, andPreProcessor, which populates a'page'meta field on each document chunk. - ›Adds
grad_acc_stepsparameter to FARMReader.train() for gradient accumulation, enabling large-model fine-tuning on memory-constrained GPUs. - ›Adds
serve_deployment_kwargskey to Pipeline YAML node definitions, supportingnum_replicas,version,ray_actor_options(num_gpus,num_cpus), andmax_concurrent_queriesfor Ray Serve deployments.
+5 moreshow less
- ›Adds
tokenizer_model_folderparameter toPreProcessorto support custom domain-specific sentence tokenizer models. - ›Adds update_document_meta() method to
InMemoryDocumentStore, aligning its interface with other document stores. - ›Adds BM25 retrieval support to the Weaviate document store.
- ›Enables
JoinDocumentsnode to handle documents withscore=None. - ›Nearly 2x performance gain for Electra reader models by eliminating a double forward-pass in the language modeling module.
└──▷ BREAKING ON UPGRADE- !Adding
update_document_metatoInMemoryDocumentStoreintroduces an interface change that may affect subclasses or code relying on the previousBaseDocumentStoremethod signatures. - !BM25 support in the Weaviate document store changes Weaviate integration behavior in a way flagged as breaking.
- !Extending the Ray Serve integration to allow
serve_deployment_kwargsattributes in Pipeline YAMLs changes the YAML schema in a breaking way. - !
MultiLabelIDs are now consistent across Python interpreters, changing previously generated ID values.
- ›Adds
- v1.6.0
Haystack v1.6.0 adds audio QA nodes, multi-hop dense retrieval, in-memory knowledge graphs, and remote model saving to HuggingFace Hub.
└──▷ GET THIS VERSION$ git clone --branch v1.6.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.6.0
└──▷ USE ITUpload a fine-tuned QA reader model to the Hugging Face Model Hub as a private repo after training.from haystack.nodes import FARMReader reader = FARMReader(model_name_or_path="roberta-base") reader.train(data_dir="my_squad_data", train_filename="squad2.json", n_epochs=1, save_dir="my_model") reader.save_to_remote(repo_id="your-user-name/roberta-base-squad2", private=True, commit_message="First version of my qa model trained with Haystack")
Run multi-hop dense retrieval over an in-memory document store to answer complex open-domain questions requiring multiple document hops.from haystack.nodes import MultihopEmbeddingRetriever from haystack.document_stores import InMemoryDocumentStore document_store = InMemoryDocumentStore() retriever = MultihopEmbeddingRetriever( document_store=document_store, embedding_model="deutschmann/mdr_roberta_q_encoder", )Load a knowledge graph from a TTL file into an in-memory store and query it with natural-language-to-SPARQL translation.from pathlib import Path from haystack.nodes import Text2SparqlRetriever from haystack.document_stores import InMemoryKnowledgeGraph kg = InMemoryKnowledgeGraph(index="tutorial10") kg.create_index() kg.import_from_ttl_file(index="tutorial10", path=Path("data/tutorial10/triples.ttl")) kgqa_retriever = Text2SparqlRetriever(knowledge_graph=kg, model_name_or_path=Path("../saved_models/tutorial10/hp_v3.4")) print(kgqa_retriever.retrieve(query="In which house is Harry Potter?"))- ›Adds
DocumentToSpeechnode for indexing pipelines that generates an audio file per document and stores it in aSpeechDocumentalongside text content (GPU recommended for indexing speed). - ›Adds
AnswerToSpeechnode for QA pipelines to generate audio of an answer on the fly fromSpeechDocuments. - ›Adds save_to_remote(repo_id, private, commit_message) method to FARMReader for uploading trained models directly to the Hugging Face Model Hub; supports
private=Trueand auth viause_auth_token=Trueon reload. - ›Adds
MultihopEmbeddingRetrievernode that applies iterative multi-hop dense retrieval with a shared encoder for query and documents, suited for complex open-domain questions requiring multiple document hops. - ›Adds
InMemoryKnowledgeGraphdocument store for storing and querying knowledge graphs without a dedicated graph database, supporting create_index() and import_from_ttl_file() for loading triples from.ttlfiles.
+1 moreshow less
- ›Adds PyTorch 1.12 and Transformers 4.20.1 compatibility, enabling accelerated training and evaluation on Apple M1 (Apple silicon) GPUs.
- ›Adds
- v1.5.0
Haystack v1.5.0 adds Generative Pseudo Labeling, batch pipeline querying, advanced eval label scopes, and DeBERTa support.
└──▷ GET THIS VERSION$ git clone --branch v1.5.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.5.0
└──▷ USE ITGenerate pseudo labels from an unlabeled domain corpus and fine-tune an EmbeddingRetriever in one workflow — no human annotation required.from haystack.nodes.retriever import EmbeddingRetriever from haystack.document_stores import InMemoryDocumentStore from haystack.nodes.question_generator.question_generator import QuestionGenerator from haystack.nodes.label_generator.pseudo_label_generator import PseudoLabelGenerator document_store = InMemoryDocumentStore() document_store.write_documents([...]) retriever = EmbeddingRetriever( document_store=document_store, embedding_model="sentence-transformers/msmarco-distilbert-base-tas-b", max_seq_len=200 ) document_store.update_embeddings(retriever) qg = QuestionGenerator(model_name_or_path="doc2query/msmarco-t5-base-v1", max_length=64, split_length=200, batch_size=12) psg = PseudoLabelGenerator(qg, retriever) output, _ = psg.run(documents=document_store.get_all_documents()) retriever.train(output["gpl_labels"])Run multiple queries through an ExtractiveQAPipeline in a single call to reduce overhead in batch evaluation or CI pipelines.from haystack.pipelines import ExtractiveQAPipeline pipe = ExtractiveQAPipeline(reader, retriever) predictions = pipe.pipeline.run_batch( queries=["Who is the father of Arya Stark?", "Who is the mother of Arya Stark?"], params={"Retriever": {"top_k": 10}, "Reader": {"top_k": 5}} )Score pipeline evaluation only when a predicted answer appears within the correct surrounding context, not just as a string match.eval_result = pipeline.eval(labels=eval_labels, params={"Retriever": {"top_k": 5}}) metrics = eval_result.calculate_metrics(answer_scope="context") print(f'Reader - F1-Score: {metrics["Reader"]["f1"]}')- ›Adds
PseudoLabelGeneratorclass inhaystack.nodes.label_generator.pseudo_label_generatorthat automatically generates pseudo labels for dense retriever fine-tuning using aQuestionGeneratorand a cross-encoder, enabling unsupervised domain adaptation without manual annotation. - ›Adds run_batch() method to every query pipeline and node (e.g. Pipeline.run_batch(), FARMReader.predict_batch()), accepting a list of queries and single or nested lists of documents to process multiple queries in one call.
- ›Adds
answer_scopeanddocument_scopeparameters to EvaluationResult.calculate_metrics(), enabling fine-grained correctness definitions such asanswer_scope='context'for context-window-bounded answer matching. - ›Adds a
sortargument toJoinAnswersnode for controlling answer ordering. - ›Adds support for DeBERTa models (e.g.
'microsoft/deberta-v3-base','microsoft/deberta-v3-large') in FARMReader, delivering F1-score improvements up to ~92% on SQuAD 2.0.
+2 moreshow less
- ›Adds training checkpoint support in the retriever trainer.
- ›Includes document metadata when computing embeddings in
EmbeddingRetriever.
└──▷ BREAKING ON UPGRADE- !Validation is now enforced for Ray pipelines, which may reject previously accepted but invalid pipeline configurations.
- !Context matching support added to pipeline.eval() changes evaluation behaviour — existing eval workflows may see different metric results.
- ›Adds
- v1.4.0
Haystack v1.4.0 adds MLflow eval tracking, FARMReader confidence filtering, Milvus2 vector+metadata queries, and BM25Retriever rename.
└──▷ GET THIS VERSION$ git clone --branch v1.4.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.4.0
└──▷ USE ITLog and compare eval results from multiple pipeline configurations to an MLflow tracking server.eval_result = Pipeline.execute_eval_run( index_pipeline=index_pipeline, query_pipeline=query_pipeline, evaluation_set_labels=labels, corpus_file_paths=file_paths, corpus_file_metas=file_metas, experiment_tracking_tool="mlflow", experiment_tracking_uri="http://localhost:5000", experiment_name="my-query-pipeline-experiment", experiment_run_name="run_1", pipeline_meta={"name": "my-pipeline-1"}, evaluation_set_meta={"name": "my-evalset"}, corpus_meta={"name": "my-corpus"}, add_isolated_node_eval=True, reuse_index=False )Filter out low-confidence reader predictions to reduce noise in QA pipeline answers.from haystack.nodes import FARMReader model = "deepset/roberta-base-squad2" reader = FARMReader(model, confidence_threshold=0.5)
- ›Adds
MLflowTrackingHeadand Pipeline.execute_eval_run() method with parametersexperiment_tracking_tool,experiment_tracking_uri,experiment_name,experiment_run_name,pipeline_meta,evaluation_set_meta,corpus_meta,add_isolated_node_eval, andreuse_indexto log evaluation metrics and pipeline artifacts to MLflow. - ›Adds
confidence_thresholdparameter to FARMReader (float between 0 and 1, disabled by default) to filter out low-confidence predictions at initialization time. - ›Adds
devicesparameter alongside existinguse_gpuin FARMReader for explicit device assignment. - ›Adds alias support in
ElasticsearchDocumentStorefor querying via index aliases. - ›Adds conjunctive query support in sparse retrieval.
+6 moreshow less
- ›Adds a flag to disable scaling scores to probabilities in retrieval.
- ›Introduces
Milvus2DocumentStore(superseding the now-deprecatedMilvus1DocumentStore) with support for filtering by scalar data types alongside vector similarity queries. - ›Renames
ElasticsearchRetrieverto BM25Retriever andElasticsearchFilterOnlyRetrievertoFilterRetriever; deprecated names remain functional until a future release. - ›Adds
EvaluationSetClientfor deepset Cloud to fetch evaluation sets. - ›Adds table linearization support in
EmbeddingRetrieverfor table inputs. - ›Adds file content-based extension detection (extracts extension based on file content rather than filename).
└──▷ BREAKING ON UPGRADE- !Return types of indexing pipeline nodes have changed.
- !
weaviate-clientis upgraded to3.3.3, which may affect existing Weaviate integrations. - !
TransformersReaderdefaults are now aligned with FARMReader, changing previous default behavior. - !Default encoding for
PDFToTextConverterchanged from Latin 1 toUTF-8. - !YAML files are now validated without loading nodes, changing pipeline validation behavior.
- ›Adds
- v1.3.0
Haystack v1.3.0 adds PineconeDocumentStore, BEIR benchmarking integration, YAML pipeline validation, and new RouteDocuments/JoinAnswers nodes.
└──▷ GET THIS VERSION$ git clone --branch v1.3.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.3.0
└──▷ USE ITValidate a pipeline YAML file before deployment to catch misconfigured components early in CI.from pathlib import Path from haystack.pipelines.config import validate_yaml validate_yaml(Path('rest_api/pipeline/pipelines.haystack-pipeline.yml'))Connect to Pinecone's managed vector database as a DocumentStore for large-scale dense retrieval without self-hosting infrastructure.import os from haystack.document_stores import PineconeDocumentStore document_store = PineconeDocumentStore(api_key=os.environ['PINECONE_API_KEY'])
Benchmark a retrieval pipeline against the BEIR 'scifact' dataset for zero-shot evaluation of retrieval quality.from haystack.pipelines import DocumentSearchPipeline, Pipeline from haystack.nodes import ElasticsearchRetriever from haystack.document_stores.elasticsearch import ElasticsearchDocumentStore document_store = ElasticsearchDocumentStore(search_fields=['content', 'name'], index='scifact_beir') retriever = ElasticsearchRetriever(document_store=document_store, top_k=1000) query_pipeline = DocumentSearchPipeline(retriever=retriever) ndcg, _map, recall, precision = Pipeline.eval_beir( index_pipeline=index_pipeline, query_pipeline=query_pipeline, dataset='scifact' )- ›Adds validate_yaml(Path(...)) from
haystack.pipelines.configto programmatically validate pipeline YAML files, identifying erroneous components and parameters. - ›Adds
PineconeDocumentStoretohaystack.document_stores, backed by Pinecone's managed vector database for large-scale dense retrieval; requires only aPINECONE_API_KEY. - ›Adds Pipeline.eval_beir() for zero-shot benchmarking of retrieval pipelines against BEIR datasets in 17 languages; available via
pip install farm-haystack[beir]. - ›Adds
RouteDocumentsandJoinAnswerspipeline nodes tohaystack.nodes. - ›Adds deploy and undeploy support for Pipelines on Deepset Cloud.
+4 moreshow less
- ›Adds
*.haystack-pipeline.ymlfile suffix convention enabling IDE schema validation and autocompletion via SchemaStore; schema published athttps://raw.githubusercontent.com/deepset-ai/haystack/master/haystack/json-schemas/haystack-pipeline.schema.json. - ›Supports
version: 'unstable'in pipeline YAML files to bypass schema validation. - ›Reintroduces
debugas a valid global key in Pipelineparams. - ›Adds bulk insert support to SQL DocumentStores.
└──▷ BREAKING ON UPGRADE- !
Milvus2DocumentStorenow requirespymilvus>=2.0.0; setups using older pymilvus versions will break. - !The
deviceparameter in internal methods is now atorch.device; code passing plain strings fordevicein affected onnxruntime paths may break.
- ›Adds validate_yaml(Path(...)) from
- v1.2.0
Haystack v1.2.0 adds brownfield Elasticsearch import, scored Tapas QA, MongoDB-style metadata filters, and new pipeline/REST capabilities.
└──▷ GET THIS VERSION$ git clone --branch v1.2.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.2.0
└──▷ USE ITMigrate an existing Elasticsearch index into a Haystack DocumentStore for immediate use in pipelines.from haystack.document_stores import InMemoryDocumentStore from haystack.utils import es_index_to_document_store document_store = es_index_to_document_store( document_store=InMemoryDocumentStore(), original_index_name="existing_index", original_content_field="content", original_name_field="name", included_metadata_fields=["date_field"], index="new_index", )Use a scored Tapas model for table-based QA where answers are ranked by confidence.from haystack.nodes import TableReader reader = TableReader(model_name_or_path="deepset/tapas-large-nq-reader", max_seq_len=512)
- ›Adds
es_index_to_document_storefunction to import existing Elasticsearch indices into any HaystackDocumentStoreby converting records to Document objects, accepting parametersoriginal_index_name,original_content_field,original_name_field,included_metadata_fields, andindex. - ›Adds
top_k_joinparameter toJoinDocuments.runto control how many documents are returned by the join node. - ›Adds
DELETE /feedbackREST API endpoint for clearing feedback/labels during testing, with label IDs now generated server-side. - ›Adds pipeline.save_to_deepset_cloud() method to push pipelines to Deepset Cloud.
- ›Adds pipeline.to_code() method to generate Python code from a pipeline definition.
+15 moreshow less
- ›Adds JSON Schema autogeneration for Pipeline YAML files, including a schema index for Schemastore.
- ›Adds YAML versioning support for Pipeline configuration files.
- ›Extends metadata filter syntax across document stores to support MongoDB-style nested boolean (
$and,$or,$not) and comparison ($eq,$in,$gt,$gte,$lt,$lte) operators; defaults to$and/$eqwhen operators are omitted, keeping existing filter expressions valid. - ›Adds
TapasForScoredQAmodel class enablingTableReaderto load Tapas models that return confidence scores (e.g.deepset/tapas-large-nq-reader,deepset/tapas-large-nq-hn-reader); answers are auto-sorted by table score then answer span score. - ›Adds reciprocal rank fusion as an additional merging method in the join node.
- ›Adds highlighting support in
ElasticsearchDocumentStore. - ›Adds
dot_productOpenSearch Script Scoring support inOpenSearchDocumentStore, includingdot_productsimilarity via HNSW. - ›Introduces read-only
DCDocumentStore(without labels support) for Deepset Cloud. - ›Adds pipeline.load_from_deepset_cloud() and pipeline listing via the Deepset Cloud SDK.
- ›Autogenerates OpenAPI specs file (
openapi.json) for the REST API, formatted as multiline for diff readability. - ›Introduces optional dependency groups for installation (e.g.
farm-haystack,farm-haystack[colab,faiss],farm-haystack[all],farm-haystack[dev]) so only required packages are installed; pip 22+ recommended. - ›Adds extended metadata filtering support to
WeaviateDocumentStorealong with more supported data types. - ›Adds extended metadata filtering support to
InMemoryDocumentStoreandSQLDocumentStore. - ›Makes
FileTypeClassifiermore flexible for routing documents by file type in pipelines. - ›Distributes intermediate layer distillation loss calculation across multiple GPUs.
└──▷ BREAKING ON UPGRADE- !Dependency management was restructured (
farm-haystacknow installs only a minimal subset by default); setups that relied on the previous all-inclusive install may be missing packages after upgrade. - !
uiandrestare now proper packages; imports or references assuming their previous module structure will break. - !
aiorwlockwas added to therayextra and maximum versions for some dependencies were pinned; environments using therayextra may need to update their dependency pins.
- ›Adds
- v1.1.0
Haystack v1.1.0 adds model distillation, isolated pipeline eval, RCIReader for TableQA, ParsrConverter, and nDCG metrics.
└──▷ GET THIS VERSION$ git clone --branch v1.1.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.1.0
└──▷ USE ITCompress a large reader into a smaller one to get near-equivalent accuracy at twice the speed.# Step 1: augment training data python augment_squad.py --squad_path squad2.json --output_path augmented_squad2.json --multiplication_factor 20 # Step 2: distil intermediate layers student.distil_intermediate_layers_from(teacher, data_dir="dataset", train_filename="augmented_squad2.json") # Step 3: distil prediction layer student.distil_prediction_layer_from(teacher, data_dir="dataset", train_filename="squad2.json")
Identify whether the retriever or reader is the accuracy bottleneck in an ExtractiveQAPipeline.eval_result = pipeline.eval(labels=eval_labels, add_isolated_node_eval=True) pipeline.print_eval_report(eval_result)
Run TableQA on large tables with meaningful confidence scores using the new RCIReader.from haystack.nodes import RCIReader reader = RCIReader( row_model_name_or_path="michaelrglass/albert-base-rci-wikisql-row", column_model_name_or_path="michaelrglass/albert-base-rci-wikisql-col" )- ›Adds student.distil_intermediate_layers_from(teacher, data_dir=..., train_filename=...) and student.distil_prediction_layer_from(teacher, data_dir=..., train_filename=...) methods to compress large reader models (teacher) into smaller models (student) via TinyBERT-style distillation, with a companion
augment_squad.py --squad_path <your dataset> --output_path <output> --multiplication_factor 20data-augmentation script. - ›Adds
add_isolated_node_eval=Trueparameter to pipeline.eval() and pipeline.print_eval_report() to expose per-node upper-bound metrics alongside integrated metrics, enabling bottleneck identification in pipelines such asExtractiveQAPipeline. - ›Adds nDCG to pipeline.eval()'s document metrics.
- ›Adds RCIReader(row_model_name_or_path=..., column_model_name_or_path=...) for TableQA using Row-Column-Intersection models, supporting larger tables and returning meaningful confidence scores unlike
TableReader. - ›Adds
ParsrConverter(based on the open-source axa-group Parsr tool) for extracting text and tables from PDF and DOCX files in a format directly usable for TableQA.
+4 moreshow less
- ›Extends
TranslationWrapperto work with QA Generation pipelines. - ›Enables batch mode for SAS cross encoders.
- ›Adds support for custom headers per request in pipeline when talking to DocumentStores.
- ›Raises an exception if Elasticsearch
search_fieldshave a wrong datatype, surfacing misconfiguration early.
└──▷ BREAKING ON UPGRADE- !Custom id hashing on DocumentStore level has changed; existing document IDs may differ after upgrade.
- !Proper foreign keys are now implemented in
MetaDocumentORMandMetaLabelORM, which may require a database migration when using PostgreSQL.
- ›Adds student.distil_intermediate_layers_from(teacher, data_dir=..., train_filename=...) and student.distil_prediction_layer_from(teacher, data_dir=..., train_filename=...) methods to compress large reader models (teacher) into smaller models (student) via TinyBERT-style distillation, with a companion
- v1.0.0
Haystack 1.0 adds Table QA, pipeline-level evaluation, per-node debug propagation, and standardized primitive objects.
└──▷ GET THIS VERSION$ git clone --branch v1.0.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v1.0.0
└──▷ USE ITRun pipeline-level evaluation and print a summary report to identify whether your Retriever or Reader is the performance bottleneck.eval_result = pipeline.eval( labels=labels, params={"Retriever": {"top_k": 5}}, ) metrics = eval_result.calculate_metrics() pipeline.print_eval_report(eval_result)Set up a Table QA pipeline to query structured table data using tri-encoder dense retrieval and TAPAS-based reading.retriever = TableTextRetriever( document_store=document_store, query_embedding_model="deepset/bert-small-mm_retrieval-question_encoder", passage_embedding_model="deepset/bert-small-mm_retrieval-passage_encoder", table_embedding_model="deepset/bert-small-mm_retrieval-table_encoder", embed_meta_fields=["title", "section_title"] ) reader = TableReader( model_name_or_path="google/tapas-base-finetuned-wtq", max_seq_len=512 )- ›New
TableTextRetrieverclass enables dense retrieval over mixed text and table corpora using three transformer encoders (query_embedding_model,passage_embedding_model,table_embedding_model). - ›New
TableReaderclass built on TAPAS performs Question Answering over table Document objects, returning single-cell answers or aggregation results; acceptsmodel_name_or_pathandmax_seq_lenarguments. - ›New Pipeline.eval() method accepts Label or
MultiLabelobjects and returns anEvaluationResultcontaining per-node, per-sample predictions in a PandasDataFrame. - ›New EvaluationResult.calculate_metrics() method computes retrieval and reader metrics from a stored
EvaluationResult. - ›New Pipeline.print_eval_report() method prints a human-readable summary of an
EvaluationResult.
+4 moreshow less
- ›Pipeline run() now accepts a top-level
debug: Trueparameter that propagates each node's input and output into the pipeline result for inspection. - ›Introduces Document, Answer, Label,
MultiLabel, and Span primitive classes as standardized inputs/outputs across all nodes, enabling IDE autocompletion and structured REST API responses. - ›New package layout exposes all Document Stores from
haystack.document_stores, all node classes fromhaystack.nodes, all pipeline classes fromhaystack.pipelines, and utilities fromhaystack.utils. - ›FARM modeling code migrated into the new
haystack/modelingpackage, removing the external FARM dependency.
└──▷ BREAKING ON UPGRADE- !The Document field
textis renamed tocontent; code writing or readingdoc['text']or Document(text=...) must switch tocontent. - !Reader nodes now return Answer objects instead of plain dicts; code unpacking keys like
answer['score']oranswer['probability']must be updated to the Answer object structure. - !Label constructor argument
questionis renamed toquery, andanswernow requires an Answer object instead of a plain string. - !The
/queryREST API response field names for offsets have changed to match the new Answer primitive format; clients parsing offset fields from v0.x responses must be updated. - !Import paths are reorganized:
haystack.document_store(singular) becomeshaystack.document_stores(plural), andhaystack.pipeline(singular) becomeshaystack.pipelines(plural); old-style imports still work but are deprecated.
- ›New
- v0.10.0
Haystack v0.10.0 adds RayPipeline for distributed scaling, SAS evaluation metric, and new FARMClassifier, SentenceTransformersRanker, and QuestionGenerator nodes.
└──▷ GET THIS VERSION$ git clone --branch v0.10.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v0.10.0
└──▷ USE ITScale a retriever-reader pipeline across a Ray cluster by assigning independent replica counts to each node.from haystack.pipeline import RayPipeline pipeline = RayPipeline.load_from_yaml(path="my_pipelines.yaml", pipeline_name="ray_query_pipeline") pipeline.run(query="What is the capital of Germany?")
Use Semantic Answer Similarity scoring during evaluation to catch semantically correct answers missed by lexical metrics.from haystack.nodes import EvalAnswers eval_reader = EvalAnswers(sas_model="sentence-transformers/paraphrase-multilingual-mpnet-base-v2")
- ›Adds
RayPipelineclass (imported fromhaystack.pipeline) enabling distributed pipeline execution across a Ray cluster, with per-nodereplicasconfigured in YAML pipeline config. - ›Adds
paramsdict argument to Pipeline.run() supporting node-targeted parameter routing such asparams={"Retriever": {"top_k": 10}, "Reader": {"top_k": 5}}. - ›Adds
sas_modelparameter toEvalAnswersnode enabling cross-encoder-based Semantic Answer Similarity (SAS) evaluation metric. - ›Adds
ImageToTextConverterandPDFToTextOCRConverterclasses providing OCR-based document conversion. - ›Adds
languageparameter toPreProcessorfor optional language-specific preprocessing.
+12 moreshow less
- ›Adds
MostSimilarDocumentsPipelinefor similarity-based document retrieval pipelines. - ›Adds FARMClassifier node for document classification at indexing time or inline in inference pipelines.
- ›Adds
SentenceTransformersRankernode for re-ranking retrieved documents using sentence-transformer models. - ›Adds
QuestionGeneratorclass for generating candidate questions from documents, supporting autosuggest and labeling acceleration use cases. - ›Adds Approximate Nearest Neighbour (ANN) search support to
OpenSearchDocumentStore. - ›Adds filter integration with KNN queries in
OpenDistroElasticsearchDocumentStore. - ›Adds multi-GPU inference support for
DensePassageRetriever. - ›Adds
idfield support in write_labels() forSQLDocumentStore. - ›Adds Crawler support for use inside indexing pipelines.
- ›Adds JSON serialization of Crawler output.
- ›Supports connecting to Elasticsearch without authentication.
- ›Adds
docs2answernode enabling FAQ-style QA and document search via the API.
└──▷ BREAKING ON UPGRADE- !The
probabilityfield is removed from answer and document results in both the Python API and REST API; onlyscore(range [0,1]) remains, populated with the formerprobabilityvalue. - !The Finder class is removed entirely.
- !Pipeline.run() no longer accepts keyword arguments like
top_k_retrieverortop_k_reader; all component params must be passed via aparamsdict (e.g.params={"Retriever": {"top_k": 10}, "Reader": {"top_k": 5}}). - !Custom pipeline nodes must no longer define
**kwargsin their run() methods and should return only the data they produce themselves.
- ›Adds
- v0.9.0
Haystack v0.9.0 adds LFQA generative QA, a Ranker node, WeaviateDocumentStore, QueryClassifier, and ONNXRuntime support.
└──▷ GET THIS VERSION$ git clone --branch v0.9.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v0.9.0
└──▷ USE ITUse WeaviateDocumentStore to combine dense retrieval with scalar tag filtering.from haystack.document_store import WeaviateDocumentStore document_store = WeaviateDocumentStore() document_store.write_documents(documents, duplicate_documents="overwrite")
- ›Adds
WeaviateDocumentStoreclass (fromhaystack.document_store) for combined vector search and scalar filtering, using Weaviate 1.4.0. - ›Adds FARMRanker node for document re-ranking via semantic similarity, composable with any retriever in a Pipeline.
- ›Adds
Seq2SeqGeneratorandRetriBERT-basedretriever for Long-Form Question Answering (LFQA), generating multi-document synthesized answers. - ›Adds
QueryClassifiernode to route keyword queries vs. natural-language questions to different pipeline branches. - ›Adds
use_ampparameter to the DPR retriever train() method to enable mixed-precision training.
+9 moreshow less
- ›Adds ONNXRuntime inference support for the Reader node.
- ›Adds options for handling duplicate documents on ingest: skip, fail, or overwrite.
- ›Adds L2 distance support for FAISS HNSW index.
- ›Adds
OpenDistrodocument store initialisation support. - ›Adds AWS Elasticsearch IAM connection support.
- ›Adds Pipeline YAML config export capability.
- ›Adds evaluation nodes for Pipelines.
- ›Adds file upload functionality and evaluation mode to the Streamlit UI.
- ›Adds a web crawler connector to ingest text directly from websites.
└──▷ BREAKING ON UPGRADE- !Python 3.6 is no longer supported; Python 3.7+ is required.
- !REST APIs have been refactored to use Pipelines, which may require changes to existing API integrations.
- !FARM bumped to 0.8.0, PyTorch to 1.8.1, and Transformers to 4.6.1 — existing environments must be updated.
- !All document stores' delete_all_documents() method has been renamed to delete_documents().
- ›Adds
- v0.8.0
Haystack v0.8.0 adds MilvusDocumentStore, Knowledge Graph QA, YAML Pipeline config, confidence scores, and a Selenium web crawler.
└──▷ GET THIS VERSION$ git clone --branch v0.8.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v0.8.0
└──▷ TRY ITQuery the new generic REST API endpoint to get answers with calibrated confidence scores from any Pipeline-backed deployment.$ curl -X POST http://localhost:8000/query \ -H 'Content-Type: application/json' \ -d '{"query": "Why did the revenue change?"}'
- ›Adds
MilvusDocumentStoreclass enabling embedding-based retrievers (DensePassageRetriever,EmbeddingRetriever) to use production-ready Milvus vector database servers for large-scale deployments. - ›Adds
GraphDBKnowlegeGraphclass for storing RDF Triples and executing SPARQL queries, integrable with the newText2SparqlRetrieverto convert natural language queries to SPARQL. - ›Introduces YAML-based Pipeline configuration via
rest_api/pipeline.yaml, enabling shareable query and indexing configs, reproducible setups, and A/B testing of Pipelines. - ›Adds new generic
POST /queryendpoint to the REST API backed by Pipelines, replacing the former/doc-qaand/faq-qaendpoints; accepts a singlequerystring and returns answers with aprobabilityconfidence score (range 0–1). - ›Adds new generic
POST /feedbackendpoint, replacing the former/doc-qa-feedbackand/faq-qa-feedbackendpoints.
+15 moreshow less
- ›Adds API endpoint to export accuracy metrics derived from user feedback.
- ›Adds a
probabilityfield (0–1) to answers, providing a calibrated model-confidence score alongside the existingscorefield. - ›Adds a Selenium-based web crawler class that accepts a list of URLs and converts extracted text into Haystack Documents.
- ›Adds
MarkdownConverterfile converter for ingesting Markdown files into Haystack document stores. - ›Adds evaluation nodes for Pipelines to measure retriever and reader performance end-to-end.
- ›Adds support for parallel paths in Pipelines, enabling branching and merging of pipeline components.
- ›Adds support for indexing Pipelines alongside existing query Pipelines.
- ›Introduces incremental embedding updates in document stores, avoiding full re-indexing when only some documents change.
- ›Adds a window-query flag to
SQLDocumentStorefor controlling passage retrieval behavior. - ›Allows non-standard tokenizers (e.g., CamemBERT) for
DensePassageRetrievervia a new argument. - ›Adds model versioning support to Haystack modeling components.
- ›Adds a SQuAD-to-DPR dataset converter for training data preparation.
- ›Adds a method to retrieve metadata values for a given key from
ElasticsearchDocumentStore. - ›Upgrades FAISS to version 1.7.0.
- ›Adds a
created_attimestamp field for documents and labels across all document stores (SQLDocumentStore,FAISSDocumentStore,ElasticsearchDocumentStore).
└──▷ BREAKING ON UPGRADE- !The
/doc-qaand/faq-qaREST API endpoints are removed and replaced by a genericPOST /queryendpoint configured viarest_api/pipeline.yaml. - !The
POST /queryendpoint now expects a singlequerystring per request instead of a list of query strings. - !The
/doc-qa-feedbackand/faq-qa-feedbackREST API endpoints are removed and replaced by a genericPOST /feedbackendpoint. - !The
createdtimestamp field on documents and labels inSQLDocumentStoreandFAISSDocumentStoreis replaced bycreated_at;ElasticsearchDocumentStorealso now hascreated_at. - !The
top_k_answersparameter in RAGenerator is renamed totop_k. - !Placeholder terms in the
custom_queryparameter forElasticsearchDocumentStoremust no longer have quotes around them.
- ›Adds
- v0.7.0
Haystack v0.7.0 adds summarization pipelines, a demo UI, batch/generator document streaming, and filter support for DensePassageRetriever.
└──▷ GET THIS VERSION$ git clone --branch v0.7.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v0.7.0
└──▷ USE ITRun a retrieve-then-summarize pipeline to display document summaries as search result previews.from haystack.pipeline import SearchSummarizationPipeline from haystack.summarizer import TransformersSummarizer summarizer = TransformersSummarizer(model_name_or_path="google/pegasus-xsum") pipe = SearchSummarizationPipeline(summarizer=summarizer, retriever=retriever) results = pipe.run(query="What caused the California wildfires?")
Update embeddings on a million-document corpus without exhausting RAM by processing in chunks.document_store.update_embeddings(retriever=retriever, batch_size=10000)
- ›Adds
batch_sizeparameters to mostDocumentStoremethods (write_documents(), update_embeddings(), get_all_documents()) to load documents in chunks and reduce memory footprint on large datasets. - ›Adds get_all_documents_generator() method to stream documents one-by-one from a document store, enabling low-memory iteration over datasets exceeding 1 million documents.
- ›Adds
TransformersSummarizerclass supporting models like PEGASUS, usable standalone via summarizer.predict(documents=docs, generate_single_summary=False) or as a pipeline node. - ›Adds
SearchSummarizationPipelinepredefined pipeline that chains retrieval and summarization in a single pipe.run() call. - ›Adds a simple demo UI for interactively testing search pipelines, inspecting API responses, and adjusting basic config params.
+2 moreshow less
- ›Adds filter support for
DensePassageRetrievercombined withInMemoryDocumentStore. - ›Adds support for a custom embedding field in
InMemoryDocumentStore.
└──▷ BREAKING ON UPGRADE- !The
index_buffer_sizeargument is removed from FAISSDocumentStore.__init__(); replace it with the newbatch_sizeargument on methods like write_documents(), update_embeddings(), and get_all_documents(). - !The
PreProcessorargumentsplit_strideis renamed tosplit_overlap; any code passingsplit_stride=Nmust be updated tosplit_overlap=N.
- ›Adds
- v0.6.0
Haystack v0.6.0 introduces DAG-based Pipelines, an OpenDistro DocumentStore, and new QA pipeline types including Generative and FAQ.
└──▷ GET THIS VERSION$ git clone --branch v0.6.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v0.6.0
└──▷ USE ITRoute incoming queries to different retrievers based on content type, then join and read — enabling conditional branching in a single pipeline.from haystack.pipeline import Pipeline, JoinDocuments class QueryClassifier: outgoing_edges = 2 def run(self, **kwargs): if '?' in kwargs['query']: return (kwargs, 'output_1') else: return (kwargs, 'output_2') pipe = Pipeline() pipe.add_node(component=QueryClassifier(), name='QueryClassifier', inputs=['Query']) pipe.add_node(component=es_retriever, name='ESRetriever', inputs=['QueryClassifier.output_1']) pipe.add_node(component=dpr_retriever, name='DPRRetriever', inputs=['QueryClassifier.output_2']) pipe.add_node(component=JoinDocuments(join_mode='concatenate'), name='JoinResults', inputs=['ESRetriever', 'DPRRetriever']) pipe.add_node(component=reader, name='QAReader', inputs=['JoinResults']) res = pipe.run(query='What did Einstein work on?', top_k_retriever=1)Run a generative QA pipeline with minimal setup using the new default pipeline classes.from haystack.pipeline import GenerativeQAPipeline pipe = GenerativeQAPipeline(generator=rag_generator, retriever=retriever) res = pipe.run(query='What causes aurora borealis?', top_k_retriever=3)
- ›Adds Pipeline class with add_node(), run(), draw(), and set_node() methods for composing search pipelines as Directed Acyclic Graphs (DAGs) with Retrievers, Readers, Generators, and custom nodes.
- ›Adds JoinDocuments(join_mode=...) node with score aggregation support to merge results from multiple Retrievers in a single Pipeline.
- ›Adds
ExtractiveQAPipeline,DocumentSearchPipeline,GenerativeQAPipeline, and FAQPipeline default pipeline classes inhaystack.pipeline, replacing the deprecated Finder class. - ›Adds
OpenDistroElasticsearchDocumentStoreto support Open Distro / AWS-hosted Elasticsearch deployments. - ›Adds
refresh_typeparameter to ElasticsearchDocumentStore.update_embeddings().
+7 moreshow less
- ›Adds
return_embeddingparameter to get_all_documents(). - ›Adds
update_existing_documentssupport to the SQL and FAISS DocumentStores. - ›Adds
filtersparameter to delete_all_documents(). - ›Adds MAP (Mean Average Precision) retriever metric for open-domain evaluation.
- ›Enables dynamic parameter updates for FARMReader at inference time.
- ›Adds GPU support for the RAG generator.
- ›Scales dot-product scores into probabilities in DocumentStore.
└──▷ BREAKING ON UPGRADE- !All
questionparameters are renamed toqueryacross Readers, Retrievers, and other components (including the predict() methods of Readers); any code passingquestion=keyword arguments will break.
- v0.5.0
Haystack v0.5.0 adds RAG-based generative QA, DPR training, MySQL support, and an Elasticsearch Query DSL-compliant REST API.
└──▷ GET THIS VERSION$ git clone --branch v0.5.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v0.5.0
└──▷ USE ITGenerate an answer from retrieved documents using RAG instead of extracting a span — useful when no single passage contains a clean answer.retrieved_docs = retriever.retrieve(query="who got the first nobel prize in physics?") predicted_result = generator.predict( question="who got the first nobel prize in physics?", documents=retrieved_docs, top_k=1 )Fine-tune a DPR retriever on domain-specific query/passage pairs to improve retrieval accuracy before production deployment.dense_passage_retriever.train( data_dir="/data/dpr", train_filename="train.json", dev_filename="dev.json", batch_size=16, embed_title=True, num_hard_negatives=1, n_epochs=3 )- ›Adds generator.predict(question=..., documents=..., top_k=...) for Retrieval Augmented Generation (RAG), enabling generative QA where answers are generated from retrieved documents rather than extracted.
- ›Adds dense_passage_retriever.train(data_dir, train_filename, dev_filename, test_filename, batch_size, embed_title, num_hard_negatives, n_epochs) to train or fine-tune DPR models on custom domain data.
- ›Adds
saveandloadmethods toDensePassageRetrieverfor persisting and reloading trained DPR models. - ›Adds
use_fast_tokenizersandsimilarity_functionparameters toDensePassageRetriever, and splitsmax_seq_leninto independentmax_seq_len_queryandmax_seq_len_passageparameters. - ›Adds
faiss_index_factory_strandreturn_embeddingparameters toFAISSDocumentStore, with new default index type'Flat'.
+11 moreshow less
- ›Adds support for MySQL databases in
DocumentStore. - ›Allows configuration of the Elasticsearch Analyzer in
ElasticsearchDocumentStore(e.g. for non-English languages). - ›Adds filter support to get_document_count() in
DocumentStore. - ›Adds Elasticsearch Query DSL-compliant Query API to the REST API.
- ›Adds
create_indexandsimilaritymetric configuration to the REST API config. - ›Allows configuration of log level in the REST API.
- ›Makes filter values optional in the REST API.
- ›Adds automatic mixed precision (AMP) support for FARMReader training.
- ›Adds a preprocessing pipeline via
PreProcessor. - ›Enables returning predictions in Finder and Retriever eval() calls.
- ›Makes creation of the label index optional in
DocumentStore.
└──▷ BREAKING ON UPGRADE- !
TransformersReaderparametermodelis replaced bymodel_name_or_path. - !
FAISSDocumentStoreparametervector_sizeis renamed tovector_dim;faiss_indextype changes fromOptional[IndexHNSWFlat]toOptional[faiss.swigfaiss.Index]; default index type changes from HNSW to'Flat'. - !
DensePassageRetrieverparametermax_seq_lenis split intomax_seq_len_query(default 64) andmax_seq_len_passage(default 256);remove_sep_tok_from_untitled_passagesparameter is removed.
- v0.4.0
Haystack v0.4.0 adds FAISSDocumentStore for scalable dense retrieval, Apache Tika file conversion, and DPR support for InMemoryDocumentStore.
└──▷ GET THIS VERSION$ git clone --branch v0.4.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout v0.4.0
└──▷ USE ITSet up a FAISS-backed document store for dense retrieval over large corpora where Elasticsearch vector search would be too slow.document_store = FAISSDocumentStore(sql_url="sqlite:///mydb.db", vector_size=768)
Extract text from a non-PDF file format (e.g., .docx or .epub) to feed into a Haystack indexing pipeline.tika_converter = TikaConverter( tika_url="http://localhost:9998/tika", remove_numeric_tables=False, remove_whitespace=False, remove_empty_lines=False, remove_header_footer=False, valid_languages=None, ) result = tika_converter.convert(file_path="documents/report.docx") print(result["text"])Control multiprocessing during reader fine-tuning to maximise CPU utilisation on a multi-core machine.reader.train( data_dir="data/squad", train_filename="train-v2.0.json", num_processes=8, )- ›Adds FAISSDocumentStore(sql_url, vector_size) for scalable approximate nearest-neighbour dense retrieval, using FAISS for embeddings and SQL for text/metadata storage.
- ›Adds TikaConverter(tika_url, remove_numeric_tables, remove_whitespace, remove_empty_lines, remove_header_footer, valid_languages) with a .convert(file_path) method to extract text from docx, pptx, html, epub, odf, and other formats via Apache Tika.
- ›Adds
refresh_typeargument toElasticsearchDocumentStore. - ›Adds
indexargument to Finder.get_answers() and Finder._via_similar_questions(). - ›Adds
num_processesparameter to reader.train() to configure multiprocessing during training.
+7 moreshow less
- ›Adds unanswerable-question support and 'no answer' aggregation to
TransformersReader. - ›Adds
MultiLabelaggregation for no-answer labels across multiple passages. - ›Adds DPR (
DensePassageRetriever) support forInMemoryDocumentStore. - ›Adds eval capability for
DensePassageRetrieverincluding refactored label/feedback handling. - ›Adds export-answers-to-CSV function.
- ›Adds option to update existing documents when indexing in document stores.
- ›Adds method to update meta fields for documents in
ElasticsearchDocumentStore.
└──▷ BREAKING ON UPGRADE- !The
databasemodule is renamed todocument_store; imports must be updated accordingly. - !The
indexingmodule is split intofile_converterandpreprocessor; imports must be updated. - !Document, Label, and Multilabel classes are moved to
schema; update imports tofrom haystack import Document, Label, Multilabel. - !File converter interface changed: Fileconverter.extract_pages(file_path=Path('...')) (which returned pages and meta) is replaced by Fileconverter.convert(file_path='...', meta={...}), which returns a dict with
text(using\fpage-break symbols) andmeta. - !
DensePassageRetrieversignature changed: now acceptsquery_embedding_modelandpassage_embedding_model(HuggingFace model hub strings) instead of the previous Facebook-codebase arguments. - !The
tagsfield on Documents is removed; filtering must now use themetafield (e.g.,{'text': 'some', 'meta': {'category': ['1', '2']}}instead of{'text': 'some', 'tags': ['category1', 'category2']}).
- 0.3.0
Haystack 0.3.0 adds Dense Passage Retrieval, pipeline evaluation, PDF/DOCX indexing, ONNXRuntime support, and a file-upload REST endpoint.
└──▷ GET THIS VERSION$ git clone --branch 0.3.0 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout 0.3.0
└──▷ USE ITUse Dense Passage Retrieval to find semantically similar passages even when query and document share no overlapping tokens.from haystack.retriever.dense import DensePassageRetriever retriever = DensePassageRetriever( document_store=document_store, embedding_model="dpr-bert-base-nq", do_lower_case=True, use_gpu=True ) results = retriever.retrieve(query="What is cosine similarity?")Benchmark your full retriever-reader pipeline to identify whether the retriever is a bottleneck and howtop_kaffects accuracy.document_store.add_eval_data("../data/nq/nq_dev_subset_v2.json") retriever.eval(top_k=10) reader.eval(document_store=document_store, device=device) finder.eval(top_k_retriever=10, top_k_reader=10)Index a PDF document into Haystack while stripping headers, footers, and numeric tables to improve retrieval quality.from haystack.indexing.file_converters.pdf import PDFToTextConverter converter = PDFToTextConverter( remove_header_footer=True, remove_numeric_tables=True, valid_languages=["de", "en"] ) pages = converter.extract_pages(file_path="report.pdf")- ›Adds
DensePassageRetrieverclass withembedding_model,do_lower_case, anduse_gpuarguments, enabling dual-encoder BERT-based retrieval that outperforms token-overlap methods when query and passage vocabulary differ. - ›Adds eval() methods to
retriever,reader, andfinder(via finder.eval(top_k_retriever=..., top_k_reader=...)) for end-to-end pipeline evaluation of recall, precision, and speed. - ›Adds document_store.add_eval_data() to load evaluation datasets (e.g. NQ-format JSON) directly into a DocumentStore for retriever and reader benchmarking.
- ›Adds
PDFToTextConverter(fromhaystack.indexing.file_converters.pdf) withremove_header_footer,remove_numeric_tables, andvalid_languagesarguments, plusDocxToTextConverter(fromhaystack.indexing.file_converters.docx), both exposing extract_pages(file_path=...) for ingesting PDF and DOCX documents. - ›Adds
BaseConverterclass with shared cleaning functions (header/footer removal, numeric table stripping) as a foundation for file-format-specific converters.
+8 moreshow less
- ›Adds ONNXRuntime support to the Reader, enabling CPU-optimised inference without GPU.
- ›Adds a REST API endpoint to upload files for indexing.
- ›Adds
EMBEDDING_MODEL_FORMATconfiguration key to the REST API config. - ›Adds a dummy retriever for benchmarking reader-only pipeline configurations.
- ›Adds tag-based filtering to
InMemoryDocumentStore. - ›Adds embedding query support to
InMemoryDocumentStore. - ›Adds custom port configuration to
ElasticsearchDocumentStore. - ›Makes the FAQ question field in DocumentStores customizable.
└──▷ BREAKING ON UPGRADE- !The
gpuinitialisation argument onDensePassageRetrieverandEmbeddingRetrieveris renamed touse_gpu; existing code passinggpu=Truewill break.
- ›Adds
- 0.2.1
Haystack 0.2.1 debuts ElasticsearchDocumentStore, embedding-based retrieval, FAQ-style QA, and a FastAPI-based modular REST API.
└──▷ GET THIS VERSION$ git clone --branch 0.2.1 https://github.com/deepset-ai/haystack.git # already have the repo? check out this version: $ git checkout 0.2.1
- ›Adds
ElasticsearchRetrieversupporting Elasticsearch native BM25 scoring and custom queries (e.g. boosting and filters). - ›Adds
EmbeddingRetrieverthat encodes texts into dense vectors (e.g. via Sentence-BERT) and retrieves via cosine similarity. - ›Adds FARMReader.train() method to fine-tune a reader on custom domain data.
- ›Adds
no_answeroption to reader results, surfacing confidence that no answer exists in the passage. - ›Adds
document_idanddocument_namefields to answer objects returned by both FARMReader andTransformersReader.
+8 moreshow less
- ›Adds
TransformersReaderas an alternative inference backend alongside the existing FARM-based reader. - ›Introduces
ElasticsearchDocumentStoreas the recommended production document store, with BM25 indexing and optional filter support. - ›Adds an in-memory document store for lightweight prototyping without an external database.
- ›Adds FAQ-style QA: index existing question-answer pairs and match incoming user questions against them to return pre-written answers.
- ›Migrates the REST API from Flask to FastAPI with modular endpoints for extractive QA, FAQ-style QA, user feedback collection/export, and APM-based request monitoring.
- ›Adds a Feedback export API endpoint for collecting and exporting user feedback on answers to build domain-specific training data.
- ›Adds Docker images (CPU and GPU variants) using Gunicorn for production deployment of the REST API.
- ›Adds optional Elastic APM integration for logging and monitoring API responses.
- ›Adds