Skip to content

Releases

v2.31.1

Choose a tag to compare

@github-actions github-actions released this 23 Jul 14:31

Release Notes

v2.31.1

Bug Fixes

  • THIS WILL LAND IN 2.31.1

v2.31.1-rc1

v2.31.1-rc1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 23 Jul 14:27

Release Notes

v2.31.1-rc1

Bug Fixes

  • THIS WILL LAND IN 2.31.1

v3.1.0

Choose a tag to compare

@github-actions github-actions released this 16 Jul 13:39

Release Notes

v3.1.0

New Features

  • NEW SHINY FEATURE FOR HAYSTACK 3.1.0

Bug Fixes

  • BUGFIX THAT GOES INTO RC2
  • BOOM! NEW IMPORTANT BUGFIX TO INCLUDE IN 3.0.1

v3.1.0-rc1

v3.1.0-rc1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 16 Jul 13:37

Release Notes

v3.1.0-rc1

New Features

  • NEW SHINY FEATURE FOR HAYSTACK 3.1.0

Bug Fixes

  • BUGFIX THAT GOES INTO RC2
  • BOOM! NEW IMPORTANT BUGFIX TO INCLUDE IN 3.0.1

v3.0.1

Choose a tag to compare

@github-actions github-actions released this 16 Jul 13:34

Release Notes

v3.0.1

Bug Fixes

  • BOOM! NEW IMPORTANT BUGFIX TO INCLUDE IN 3.0.1

v3.0.1-rc1

v3.0.1-rc1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 16 Jul 13:32

Release Notes

v3.0.1-rc1

Bug Fixes

  • BOOM! NEW IMPORTANT BUGFIX TO INCLUDE IN 3.0.1

v3.0.0

Choose a tag to compare

@github-actions github-actions released this 16 Jul 13:21

Release Notes

v3.0.0

Upgrade Notes

  • AsyncPipeline has been removed. Its asynchronous capabilities are now part of the single Pipeline class, which provides both a synchronous run method and the asynchronous run_async, run_async_generator, and stream methods (mirroring how components expose run and run_async).

    To migrate, replace AsyncPipeline with Pipeline:

    # Before
    from haystack import AsyncPipeline
    pipeline = AsyncPipeline()
    result = await pipeline.run_async(data)
    
    # After
    from haystack import Pipeline
    pipeline = Pipeline()
    result = await pipeline.run_async(data)

    Serialized pipelines, SuperComponents, and PipelineTools are loaded as Pipeline instances.

    Pipeline.run runs synchronously and blocks until completion; in an async context use await pipeline.run_async(...). The former AsyncPipeline.run was a synchronous wrapper around the concurrent engine, so to preserve that behavior use asyncio.run(pipeline.run_async(...)). Note that Pipeline.run executes components sequentially and does not accept concurrency_limit.

    Both synchronous and asynchronous runs are traced under a single haystack.pipeline.run operation name, distinguished by a haystack.pipeline.execution_mode tag (sync or async). Previously, asynchronous runs used the haystack.async_pipeline.run operation name.

  • continue_run is now a reserved key in Agent.state_schema (set by an on_exit hook to keep the Agent running). Passing it via the state_schema argument now raises ValueError. Rename any conflicting state key (e.g. my_continue_run) to migrate.

  • DocumentNDCGEvaluator now matches documents by their content field by default instead of their auto-generated id. Previously, ground truth and retrieved documents were matched only if they had identical id values, which rarely happened in practice since IDs are generated independently for each Document instance. As a result, NDCG scores computed with this evaluator may change for existing pipelines. To keep the previous id-based matching behavior, pass document_comparison_field="id" when constructing the evaluator.

  • Agent no longer validates exit_conditions against tool names at initialization. Exit conditions are now evaluated at runtime, so a condition naming a tool that is loaded later — e.g. a tool passed at runtime or one discovered by SearchableToolset — works as expected. Consequently, an unknown exit-condition name no longer raises a ValueError at construction.

  • Tool support is now validated against the tools resolved for each run, not only the tools provided at initialization. Passing tools at runtime via run(tools=...) to a chat generator that does not support tools now raises a TypeError instead of silently ignoring them.

  • Components in Haystack that use external resources now consistently create those resources during warm-up rather than in __init__. As a result, some components that previously raised errors during initialization (for example, because of a missing API key) now raise them during warm_up instead. Similar changes will be rolled out to components in Haystack Core Integrations in the future.

  • step_count, token_usage, and tool_call_counts are now reserved keys in Agent.state_schema. Passing any of them via the state_schema argument now raises ValueError. Rename the conflicting state key (e.g. my_token_usage) to migrate.

  • GeneratedAnswer and ExtractedAnswer now serialize to the same flat format as all other Haystack dataclasses (Document, ChatMessage, etc.). This is a breaking change: to_dict() no longer wraps the fields in the {"type": "...", "init_parameters": {...}} envelope and instead returns the fields at the top level. Any code or stored artifact that reads the serialized output and expects the type/init_parameters keys must be updated to read the fields directly. Deserialization is backward compatible: from_dict() accepts both the new flat format and the old wrapped {"type": "...", "init_parameters": {...}} format, so existing pipeline snapshots, breakpoints, and State objects keep loading without any changes.

  • Confirmation strategies now receive the model-requested tool arguments in tool_params rather than the fully-prepared arguments. Values injected from State via a tool's inputs_from_state mapping (and the State object injected into State-typed parameters) are no longer included in what is presented for confirmation — that injection now happens only at tool execution time. If your ConfirmationUI or ConfirmationPolicy displayed or inspected those state-injected values, it will now see only the arguments the model provided.

  • The hash used to auto-generate Document.id is now computed from a canonical (key-sorted) JSON serialization of meta. Documents with empty meta are unaffected, but most other documents will get different IDs than they did before:

    • documents with non-empty meta (the serialization changes from dict's repr to JSON);
    • documents whose meta contains non-JSON-serializable values such as datetime or custom classes (these are now serialized via str(...) rather than repr(...), e.g. "2024-01-01 00:00:00" instead of "datetime.datetime(2024, 1, 1, 0, 0)").

    If you rely on auto-generated IDs to match documents already persisted in a DocumentStore, you will need to re-ingest the affected documents (or pass the previous id explicitly when constructing the Document).

  • Pipeline deserialization (Pipeline.load / Pipeline.loads / Pipeline.from_dict) now refuses to import classes from modules outside a trusted-module allowlist. By default the allowlist contains haystack, haystack_integrations, haystack_experimental, builtins, typing, and collections. Pipelines that reference custom components or callables in other packages will fail to load with a DeserializationError until the additional modules are added to the allowlist.

    To restore loading of such pipelines, extend the allowlist via one of:

    • per-call kwarg: Pipeline.load(fp, allowed_modules=["mypkg.*"]);
    • process-wide programmatic API: from haystack.core.serialization import allow_deserialization_module;
    • environment variable: HAYSTACK_DESERIALIZATION_ALLOWLIST="mypkg.*,otherpkg.*";
    • or, if the source of the serialized data is fully trusted, bypass the allowlist with Pipeline.load(fp, unsafe=True).
  • During pipeline deserialization, default_from_dict now validates the keys of init_parameters against the parent class's __init__ signature before recursing into any nested {"type": "...", "init_parameters": {...}} dictionary. A nested dict whose key is not an accepted parameter name on the parent class is rejected with a DeserializationError before the nested type is imported. Classes whose constructor takes **kwargs are exempt, since their accepted parameter set cannot be statically determined.

    This may surface pre-existing YAML bugs — e.g. typos, leftovers from renamed or removed parameters, or stale snapshots from older Haystack versions. The fix is to update the YAML so each nested-component key matches a real __init__ parameter on the parent class.

  • PromptBuilder and ChatPromptBuilder now treat every Jinja2 template variable as required by default. Previously, variables were optional by default unless explicitly listed in required_variables.

    To check if you're affected: look for PromptBuilder(...) or ChatPromptBuilder(...) calls in your code that do not pass required_variables. If those components relied on missing variables being silently rendered as empty strings, the behavior depends on how the missing variable was reaching the builder:

    • Calling builder.run() directly without providing a required variable now raises ValueError.
    • In a pipeline, if a required variable is neither provided as a user input nor connected from another component, Pipeline.run raises ValueError("Missing mandatory input '<var>' for component '<name>'.").
    • In a pipeline, if a required variable is connected to a sender that never produces output on a given run (for example, a loop-fed input on the first iteration or a conditional branch that isn't taken), the pipeline cannot make progress and raises PipelineComponentsBlockedError.

    Migration options:

    • To require only a subset of variables, pass required_variables=["var1", "var2"].
    • To preserve the previous "all optional" behavior, explicitly pass required_variables=None.
    • To require everything (the new default), no change is needed.
  • Human-in-the-Loop tool confirmation on Agent is now expressed as a before_tool hook instead of a dedicated concept. The confirmation_strategies and confirmation_strategy_context parameters have been removed from Agent.__init__, Agent.run, and Agent.run_async. To migrate, wrap your confirmation strategies in the new ConfirmationHook, register it under the before_tool hook point, and pass any request-scoped resources through the generic hook_context run argument instead of confirmation_strategy_context. The Human-in-the-Loop module has also moved from haystack.human_in_the_loop to haystack.hooks.human_in_the_loop, so that it lives alongside the other built-in hooks (such as tool result offloading); update your imports to the new location:

    from haystack.components.agents import Agent
    from haystack.hooks.human_in_the_loop import (
        AlwaysAskPolicy,
        BlockingConfirmationStrategy,
        ConfirmationHook,
        RichConsoleUI,
    )
    
    confirmation_hook = ConfirmationHook(
        confirmation_strategies={
            "my_tool": BlockingConfirmationStrategy(
                confirmation_policy=AlwaysAskPolicy(), confirmation_ui=RichConsoleUI()
            )
        }
    )
    agent = Agent(chat_generator=..., tools=[...], hooks={"before_tool": [confirmation_hook]})...
Read more

v3.0.0-rc2

v3.0.0-rc2 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 16 Jul 13:17

Release Notes

v3.0.0-rc2

Bug Fixes

  • BUGFIX THAT GOES INTO RC2

v3.0.0-rc1

Upgrade Notes

  • AsyncPipeline has been removed. Its asynchronous capabilities are now part of the single Pipeline class, which provides both a synchronous run method and the asynchronous run_async, run_async_generator, and stream methods (mirroring how components expose run and run_async).

    To migrate, replace AsyncPipeline with Pipeline:

    # Before
    from haystack import AsyncPipeline
    pipeline = AsyncPipeline()
    result = await pipeline.run_async(data)
    
    # After
    from haystack import Pipeline
    pipeline = Pipeline()
    result = await pipeline.run_async(data)

    Serialized pipelines, SuperComponents, and PipelineTools are loaded as Pipeline instances.

    Pipeline.run runs synchronously and blocks until completion; in an async context use await pipeline.run_async(...). The former AsyncPipeline.run was a synchronous wrapper around the concurrent engine, so to preserve that behavior use asyncio.run(pipeline.run_async(...)). Note that Pipeline.run executes components sequentially and does not accept concurrency_limit.

    Both synchronous and asynchronous runs are traced under a single haystack.pipeline.run operation name, distinguished by a haystack.pipeline.execution_mode tag (sync or async). Previously, asynchronous runs used the haystack.async_pipeline.run operation name.

  • continue_run is now a reserved key in Agent.state_schema (set by an on_exit hook to keep the Agent running). Passing it via the state_schema argument now raises ValueError. Rename any conflicting state key (e.g. my_continue_run) to migrate.

  • DocumentNDCGEvaluator now matches documents by their content field by default instead of their auto-generated id. Previously, ground truth and retrieved documents were matched only if they had identical id values, which rarely happened in practice since IDs are generated independently for each Document instance. As a result, NDCG scores computed with this evaluator may change for existing pipelines. To keep the previous id-based matching behavior, pass document_comparison_field="id" when constructing the evaluator.

  • Agent no longer validates exit_conditions against tool names at initialization. Exit conditions are now evaluated at runtime, so a condition naming a tool that is loaded later — e.g. a tool passed at runtime or one discovered by SearchableToolset — works as expected. Consequently, an unknown exit-condition name no longer raises a ValueError at construction.

  • Tool support is now validated against the tools resolved for each run, not only the tools provided at initialization. Passing tools at runtime via run(tools=...) to a chat generator that does not support tools now raises a TypeError instead of silently ignoring them.

  • Components in Haystack that use external resources now consistently create those resources during warm-up rather than in __init__. As a result, some components that previously raised errors during initialization (for example, because of a missing API key) now raise them during warm_up instead. Similar changes will be rolled out to components in Haystack Core Integrations in the future.

  • step_count, token_usage, and tool_call_counts are now reserved keys in Agent.state_schema. Passing any of them via the state_schema argument now raises ValueError. Rename the conflicting state key (e.g. my_token_usage) to migrate.

  • Confirmation strategies now receive the model-requested tool arguments in tool_params rather than the fully-prepared arguments. Values injected from State via a tool's inputs_from_state mapping (and the State object injected into State-typed parameters) are no longer included in what is presented for confirmation — that injection now happens only at tool execution time. If your ConfirmationUI or ConfirmationPolicy displayed or inspected those state-injected values, it will now see only the arguments the model provided.

  • The hash used to auto-generate Document.id is now computed from a canonical (key-sorted) JSON serialization of meta. Documents with empty meta are unaffected, but most other documents will get different IDs than they did before:

    • documents with non-empty meta (the serialization changes from dict's repr to JSON);
    • documents whose meta contains non-JSON-serializable values such as datetime or custom classes (these are now serialized via str(...) rather than repr(...), e.g. "2024-01-01 00:00:00" instead of "datetime.datetime(2024, 1, 1, 0, 0)").

    If you rely on auto-generated IDs to match documents already persisted in a DocumentStore, you will need to re-ingest the affected documents (or pass the previous id explicitly when constructing the Document).

  • Pipeline deserialization (Pipeline.load / Pipeline.loads / Pipeline.from_dict) now refuses to import classes from modules outside a trusted-module allowlist. By default the allowlist contains haystack, haystack_integrations, haystack_experimental, builtins, typing, and collections. Pipelines that reference custom components or callables in other packages will fail to load with a DeserializationError until the additional modules are added to the allowlist.

    To restore loading of such pipelines, extend the allowlist via one of:

    • per-call kwarg: Pipeline.load(fp, allowed_modules=["mypkg.*"]);
    • process-wide programmatic API: from haystack.core.serialization import allow_deserialization_module;
    • environment variable: HAYSTACK_DESERIALIZATION_ALLOWLIST="mypkg.*,otherpkg.*";
    • or, if the source of the serialized data is fully trusted, bypass the allowlist with Pipeline.load(fp, unsafe=True).
  • During pipeline deserialization, default_from_dict now validates the keys of init_parameters against the parent class's __init__ signature before recursing into any nested {"type": "...", "init_parameters": {...}} dictionary. A nested dict whose key is not an accepted parameter name on the parent class is rejected with a DeserializationError before the nested type is imported. Classes whose constructor takes **kwargs are exempt, since their accepted parameter set cannot be statically determined.

    This may surface pre-existing YAML bugs — e.g. typos, leftovers from renamed or removed parameters, or stale snapshots from older Haystack versions. The fix is to update the YAML so each nested-component key matches a real __init__ parameter on the parent class.

  • PromptBuilder and ChatPromptBuilder now treat every Jinja2 template variable as required by default. Previously, variables were optional by default unless explicitly listed in required_variables.

    To check if you're affected: look for PromptBuilder(...) or ChatPromptBuilder(...) calls in your code that do not pass required_variables. If those components relied on missing variables being silently rendered as empty strings, the behavior depends on how the missing variable was reaching the builder:

    • Calling builder.run() directly without providing a required variable now raises ValueError.
    • In a pipeline, if a required variable is neither provided as a user input nor connected from another component, Pipeline.run raises ValueError("Missing mandatory input '<var>' for component '<name>'.").
    • In a pipeline, if a required variable is connected to a sender that never produces output on a given run (for example, a loop-fed input on the first iteration or a conditional branch that isn't taken), the pipeline cannot make progress and raises PipelineComponentsBlockedError.

    Migration options:

    • To require only a subset of variables, pass required_variables=["var1", "var2"].
    • To preserve the previous "all optional" behavior, explicitly pass required_variables=None.
    • To require everything (the new default), no change is needed.
  • Human-in-the-Loop tool confirmation on Agent is now expressed as a before_tool hook instead of a dedicated concept. The confirmation_strategies and confirmation_strategy_context parameters have been removed from Agent.__init__, Agent.run, and Agent.run_async. To migrate, wrap your confirmation strategies in the new ConfirmationHook, register it under the before_tool hook point, and pass any request-scoped resources through the generic hook_context run argument instead of confirmation_strategy_context. The Human-in-the-Loop module has also moved from haystack.human_in_the_loop to haystack.hooks.human_in_the_loop, so that it lives alongside the other built-in hooks (such as tool result offloading); update your imports to the new location:

    from haystack.components.agents import Agent
    from haystack.hooks.human_in_the_loop import (
        AlwaysAskPolicy,
        BlockingConfirmationStrategy,
        ConfirmationHook,
        RichConsoleUI,
    )
    
    confirmation_hook = ConfirmationHook(
        confirmation_strategies={
            "my_tool": BlockingConfirmationStrategy(
                confirmation_policy=AlwaysAskPolicy(), confirmation_ui=RichConsoleUI()
            )
        }
    )
    agent = Agent(chat_generator=..., tools=[...], hooks={"before_tool": [confirmation_hook]})
    agent.run(messages=[...], hook_context={"websocket": ws})
  • The agent-specific breakpoint API has been removed. The AgentBreakpoint, ToolBreakpoint, and AgentSnapshot dataclasses are no longer exported from haystack.dataclasses, and the break_point, snapshot, and snapshot_callback parameters have been removed from Agent.run and Agent.run_async. Pipeline.run no longer accepts AgentBreakpoint for its break_point argument, and the agent_snapshot field has been removed from PipelineSnapshot. Pipeline-level breakpoints via Breakpoint and PipelineSnapshot continue to work as before, but pausing and resuming execution inside an A...

Read more

v3.0.0-rc1

v3.0.0-rc1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 16 Jul 13:09

Release Notes

v3.0.0-rc1

Upgrade Notes

  • AsyncPipeline has been removed. Its asynchronous capabilities are now part of the single Pipeline class, which provides both a synchronous run method and the asynchronous run_async, run_async_generator, and stream methods (mirroring how components expose run and run_async).

    To migrate, replace AsyncPipeline with Pipeline:

    # Before
    from haystack import AsyncPipeline
    pipeline = AsyncPipeline()
    result = await pipeline.run_async(data)
    
    # After
    from haystack import Pipeline
    pipeline = Pipeline()
    result = await pipeline.run_async(data)

    Serialized pipelines, SuperComponents, and PipelineTools are loaded as Pipeline instances.

    Pipeline.run runs synchronously and blocks until completion; in an async context use await pipeline.run_async(...). The former AsyncPipeline.run was a synchronous wrapper around the concurrent engine, so to preserve that behavior use asyncio.run(pipeline.run_async(...)). Note that Pipeline.run executes components sequentially and does not accept concurrency_limit.

    Both synchronous and asynchronous runs are traced under a single haystack.pipeline.run operation name, distinguished by a haystack.pipeline.execution_mode tag (sync or async). Previously, asynchronous runs used the haystack.async_pipeline.run operation name.

  • continue_run is now a reserved key in Agent.state_schema (set by an on_exit hook to keep the Agent running). Passing it via the state_schema argument now raises ValueError. Rename any conflicting state key (e.g. my_continue_run) to migrate.

  • DocumentNDCGEvaluator now matches documents by their content field by default instead of their auto-generated id. Previously, ground truth and retrieved documents were matched only if they had identical id values, which rarely happened in practice since IDs are generated independently for each Document instance. As a result, NDCG scores computed with this evaluator may change for existing pipelines. To keep the previous id-based matching behavior, pass document_comparison_field="id" when constructing the evaluator.

  • Agent no longer validates exit_conditions against tool names at initialization. Exit conditions are now evaluated at runtime, so a condition naming a tool that is loaded later — e.g. a tool passed at runtime or one discovered by SearchableToolset — works as expected. Consequently, an unknown exit-condition name no longer raises a ValueError at construction.

  • Tool support is now validated against the tools resolved for each run, not only the tools provided at initialization. Passing tools at runtime via run(tools=...) to a chat generator that does not support tools now raises a TypeError instead of silently ignoring them.

  • Components in Haystack that use external resources now consistently create those resources during warm-up rather than in __init__. As a result, some components that previously raised errors during initialization (for example, because of a missing API key) now raise them during warm_up instead. Similar changes will be rolled out to components in Haystack Core Integrations in the future.

  • step_count, token_usage, and tool_call_counts are now reserved keys in Agent.state_schema. Passing any of them via the state_schema argument now raises ValueError. Rename the conflicting state key (e.g. my_token_usage) to migrate.

  • Confirmation strategies now receive the model-requested tool arguments in tool_params rather than the fully-prepared arguments. Values injected from State via a tool's inputs_from_state mapping (and the State object injected into State-typed parameters) are no longer included in what is presented for confirmation — that injection now happens only at tool execution time. If your ConfirmationUI or ConfirmationPolicy displayed or inspected those state-injected values, it will now see only the arguments the model provided.

  • The hash used to auto-generate Document.id is now computed from a canonical (key-sorted) JSON serialization of meta. Documents with empty meta are unaffected, but most other documents will get different IDs than they did before:

    • documents with non-empty meta (the serialization changes from dict's repr to JSON);
    • documents whose meta contains non-JSON-serializable values such as datetime or custom classes (these are now serialized via str(...) rather than repr(...), e.g. "2024-01-01 00:00:00" instead of "datetime.datetime(2024, 1, 1, 0, 0)").

    If you rely on auto-generated IDs to match documents already persisted in a DocumentStore, you will need to re-ingest the affected documents (or pass the previous id explicitly when constructing the Document).

  • Pipeline deserialization (Pipeline.load / Pipeline.loads / Pipeline.from_dict) now refuses to import classes from modules outside a trusted-module allowlist. By default the allowlist contains haystack, haystack_integrations, haystack_experimental, builtins, typing, and collections. Pipelines that reference custom components or callables in other packages will fail to load with a DeserializationError until the additional modules are added to the allowlist.

    To restore loading of such pipelines, extend the allowlist via one of:

    • per-call kwarg: Pipeline.load(fp, allowed_modules=["mypkg.*"]);
    • process-wide programmatic API: from haystack.core.serialization import allow_deserialization_module;
    • environment variable: HAYSTACK_DESERIALIZATION_ALLOWLIST="mypkg.*,otherpkg.*";
    • or, if the source of the serialized data is fully trusted, bypass the allowlist with Pipeline.load(fp, unsafe=True).
  • During pipeline deserialization, default_from_dict now validates the keys of init_parameters against the parent class's __init__ signature before recursing into any nested {"type": "...", "init_parameters": {...}} dictionary. A nested dict whose key is not an accepted parameter name on the parent class is rejected with a DeserializationError before the nested type is imported. Classes whose constructor takes **kwargs are exempt, since their accepted parameter set cannot be statically determined.

    This may surface pre-existing YAML bugs — e.g. typos, leftovers from renamed or removed parameters, or stale snapshots from older Haystack versions. The fix is to update the YAML so each nested-component key matches a real __init__ parameter on the parent class.

  • PromptBuilder and ChatPromptBuilder now treat every Jinja2 template variable as required by default. Previously, variables were optional by default unless explicitly listed in required_variables.

    To check if you're affected: look for PromptBuilder(...) or ChatPromptBuilder(...) calls in your code that do not pass required_variables. If those components relied on missing variables being silently rendered as empty strings, the behavior depends on how the missing variable was reaching the builder:

    • Calling builder.run() directly without providing a required variable now raises ValueError.
    • In a pipeline, if a required variable is neither provided as a user input nor connected from another component, Pipeline.run raises ValueError("Missing mandatory input '<var>' for component '<name>'.").
    • In a pipeline, if a required variable is connected to a sender that never produces output on a given run (for example, a loop-fed input on the first iteration or a conditional branch that isn't taken), the pipeline cannot make progress and raises PipelineComponentsBlockedError.

    Migration options:

    • To require only a subset of variables, pass required_variables=["var1", "var2"].
    • To preserve the previous "all optional" behavior, explicitly pass required_variables=None.
    • To require everything (the new default), no change is needed.
  • Human-in-the-Loop tool confirmation on Agent is now expressed as a before_tool hook instead of a dedicated concept. The confirmation_strategies and confirmation_strategy_context parameters have been removed from Agent.__init__, Agent.run, and Agent.run_async. To migrate, wrap your confirmation strategies in the new ConfirmationHook, register it under the before_tool hook point, and pass any request-scoped resources through the generic hook_context run argument instead of confirmation_strategy_context. The Human-in-the-Loop module has also moved from haystack.human_in_the_loop to haystack.hooks.human_in_the_loop, so that it lives alongside the other built-in hooks (such as tool result offloading); update your imports to the new location:

    from haystack.components.agents import Agent
    from haystack.hooks.human_in_the_loop import (
        AlwaysAskPolicy,
        BlockingConfirmationStrategy,
        ConfirmationHook,
        RichConsoleUI,
    )
    
    confirmation_hook = ConfirmationHook(
        confirmation_strategies={
            "my_tool": BlockingConfirmationStrategy(
                confirmation_policy=AlwaysAskPolicy(), confirmation_ui=RichConsoleUI()
            )
        }
    )
    agent = Agent(chat_generator=..., tools=[...], hooks={"before_tool": [confirmation_hook]})
    agent.run(messages=[...], hook_context={"websocket": ws})
  • The agent-specific breakpoint API has been removed. The AgentBreakpoint, ToolBreakpoint, and AgentSnapshot dataclasses are no longer exported from haystack.dataclasses, and the break_point, snapshot, and snapshot_callback parameters have been removed from Agent.run and Agent.run_async. Pipeline.run no longer accepts AgentBreakpoint for its break_point argument, and the agent_snapshot field has been removed from PipelineSnapshot. Pipeline-level breakpoints via Breakpoint and PipelineSnapshot continue to work as before, but pausing and resuming execution inside an Agent (at the chat generator or tool invoker) is no longer s...

Read more