# `Jido.AI.Reasoning.Helpers`
[🔗](https://github.com/agentjido/jido_ai/blob/v2.4.0/lib/jido_ai/reasoning/helpers.ex#L1)

Helper functions for creating StateOps in Jido.AI strategies.

This module provides convenient helpers for common state operation patterns
used across strategies. It wraps `Jido.Agent.StateOp` constructors with
strategy-specific semantics.

## StateOp Types

* `SetState` - Deep merge attributes into state
* `SetPath` - Set value at nested path
* `DeleteKeys` - Remove top-level keys
* `DeletePath` - Delete value at nested path

## Usage

    state_ops = [
      Helpers.set_strategy_status(:running),
      Helpers.increment_iteration(),
      Helpers.append_to_conversation(message)
    ]

# `state_op`

```elixir
@type state_op() ::
  Jido.Agent.StateOp.SetState.t()
  | Jido.Agent.StateOp.SetPath.t()
  | Jido.Agent.StateOp.DeleteKeys.t()
  | Jido.Agent.StateOp.DeletePath.t()
```

# `strategy_ctx`

```elixir
@type strategy_ctx() :: map()
```

# `action_context`

```elixir
@spec action_context(Jido.Agent.t(), strategy_ctx()) :: map()
```

Builds normalized context for plugin-routed action execution.

Guarantees the `state`, `agent`, and `plugin_state` keys are present.

# `add_pending_tool`

```elixir
@spec add_pending_tool(map()) :: Jido.Agent.StateOp.SetState.t()
```

Creates a StateOp to add a pending tool call.

## Examples

    iex> tool = %{id: "call_1", name: "search", arguments: %{query: "test"}}
    iex> Helpers.add_pending_tool(tool)
    %Jido.Agent.StateOp.SetState{attrs: %{pending_tool_calls: [%{id: "call_1", name: "search", arguments: %{query: "test"}}]}}

# `append_conversation`

```elixir
@spec append_conversation([map()]) :: Jido.Agent.StateOp.SetState.t()
```

Creates a StateOp to append a message to the conversation.

## Examples

    iex> message = %{role: :user, content: "Hello"}
    iex> Helpers.append_conversation([message])
    %Jido.Agent.StateOp.SetState{attrs: %{conversation: [%{role: :user, content: "Hello"}]}}

# `append_streaming_text`

```elixir
@spec append_streaming_text(String.t()) :: Jido.Agent.StateOp.SetPath.t()
```

Creates a StateOp to append to the streaming text.

## Examples

    iex> Helpers.append_streaming_text(" world")
    %Jido.Agent.StateOp.SetPath{path: [:streaming_text], value: " world"}

# `apply_to_state`

```elixir
@spec apply_to_state(map(), [state_op()]) :: map()
```

Applies state operations to a state map.

This is useful for strategies to apply state operations internally
before setting the strategy state on the agent.

## Examples

    iex> ops = [Jido.Agent.StateOp.set_path([:status], :running)]
    iex> Helpers.apply_to_state(%{iteration: 1}, ops)
    %{iteration: 1, status: :running}

# `clear_call_id`

```elixir
@spec clear_call_id() :: Jido.Agent.StateOp.DeletePath.t()
```

Creates a StateOp to clear the current LLM call ID.

## Examples

    iex> Helpers.clear_call_id()
    %Jido.Agent.StateOp.DeletePath{path: [:current_llm_call_id]}

# `clear_pending_tools`

```elixir
@spec clear_pending_tools() :: Jido.Agent.StateOp.SetState.t()
```

Creates a StateOp to clear pending tool calls.

## Examples

    iex> Helpers.clear_pending_tools()
    %Jido.Agent.StateOp.SetState{attrs: %{pending_tool_calls: []}}

# `compose`

```elixir
@spec compose([state_op()]) :: [state_op()]
```

Composes multiple state operations into a single list.

This is a convenience function for building state operation lists.

## Examples

    iex> Helpers.compose([
    ...>   Helpers.set_iteration_status(:running),
    ...>   Helpers.set_iteration(1)
    ...> ])
    [%Jido.Agent.StateOp.SetPath{path: [:status], value: :running}, %Jido.Agent.StateOp.SetPath{path: [:iteration], value: 1}]

# `delete_keys`

```elixir
@spec delete_keys([atom()]) :: Jido.Agent.StateOp.DeleteKeys.t()
```

Creates a StateOp to delete specific keys from strategy state.

## Examples

    iex> Helpers.delete_keys([:temp1, :temp2])
    %Jido.Agent.StateOp.DeleteKeys{keys: [:temp1, :temp2]}

# `delete_temp_keys`

```elixir
@spec delete_temp_keys() :: Jido.Agent.StateOp.DeleteKeys.t()
```

Creates a StateOp to delete temporary keys from strategy state.

## Examples

    iex> Helpers.delete_temp_keys()
    %Jido.Agent.StateOp.DeleteKeys{keys: [:temp, :cache, :ephemeral]}

# `execute_action_instruction`

```elixir
@spec execute_action_instruction(Jido.Agent.t(), Jido.Instruction.t(), strategy_ctx()) ::
  {Jido.Agent.t(), [struct()]}
```

Executes a non-strategy `Jido.Action` instruction using direct strategy semantics.

This is used as a fallback path in strategy adapters so plugin-routed action modules
can run even when the strategy does not have a dedicated command atom.

# `maybe_execute_action_instruction`

```elixir
@spec maybe_execute_action_instruction(
  Jido.Agent.t(),
  Jido.Instruction.t(),
  strategy_ctx()
) ::
  {Jido.Agent.t(), [struct()]} | :noop
```

Executes an instruction if its action is a loadable module implementing `run/2`.

Returns `:noop` when the action is not an executable module.

# `prepend_conversation`

```elixir
@spec prepend_conversation(map(), [map()]) :: Jido.Agent.StateOp.SetState.t()
```

Creates a StateOp to prepend a message to the conversation.

## Examples

    iex> message = %{role: :user, content: "Hello"}
    iex> current_conversation = [%{role: :assistant, content: "Hi"}]
    iex> Helpers.prepend_conversation(message, current_conversation)
    %Jido.Agent.StateOp.SetState{attrs: %{conversation: [%{role: :user, content: "Hello"}, %{role: :assistant, content: "Hi"}]}}

# `remove_pending_tool`

```elixir
@spec remove_pending_tool(String.t()) :: Jido.Agent.StateOp.DeletePath.t()
```

Creates a StateOp to remove a specific pending tool by ID.

## Examples

    iex> Helpers.remove_pending_tool("call_1")
    %Jido.Agent.StateOp.DeletePath{path: [:pending_tool_calls, "call_1"]}

Note: This operation is meant for map-based pending_tool_calls.
For list-based pending_tool_calls, use filter_pending_tools/1 instead.

# `reset_strategy_state`

```elixir
@spec reset_strategy_state() :: Jido.Agent.StateOp.ReplaceState.t()
```

Creates a StateOp to reset the strategy state to initial values.

## Examples

    iex> result = Helpers.reset_strategy_state()
    iex> result.state.status == :idle and result.state.iteration == 0
    true

# `set_call_id`

```elixir
@spec set_call_id(String.t()) :: Jido.Agent.StateOp.SetPath.t()
```

Creates a StateOp to set the current LLM call ID.

## Examples

    iex> Helpers.set_call_id("call_123")
    %Jido.Agent.StateOp.SetPath{path: [:current_llm_call_id], value: "call_123"}

# `set_config_field`

```elixir
@spec set_config_field(atom(), term()) :: Jido.Agent.StateOp.SetPath.t()
```

Creates a StateOp to set a specific config field.

## Examples

    iex> Helpers.set_config_field(:tools, [MyAction])
    %Jido.Agent.StateOp.SetPath{path: [:config, :tools], value: [MyAction]}

# `set_conversation`

```elixir
@spec set_conversation([map()]) :: Jido.Agent.StateOp.SetState.t()
```

Creates a StateOp to set the entire conversation.

## Examples

    iex> messages = [%{role: :user, content: "Hello"}, %{role: :assistant, content: "Hi"}]
    iex> Helpers.set_conversation(messages)
    %Jido.Agent.StateOp.SetState{attrs: %{conversation: messages}}

# `set_final_answer`

```elixir
@spec set_final_answer(String.t()) :: Jido.Agent.StateOp.SetPath.t()
```

Creates a StateOp to set the final answer.

## Examples

    iex> Helpers.set_final_answer("42")
    %Jido.Agent.StateOp.SetPath{path: [:final_answer], value: "42"}

# `set_iteration`

```elixir
@spec set_iteration(non_neg_integer()) :: Jido.Agent.StateOp.SetPath.t()
```

Creates a StateOp to increment the iteration counter.

Note: This cannot directly read the current value, so it should be used
with the current iteration value known from context.

## Examples

    iex> Helpers.set_iteration(5)
    %Jido.Agent.StateOp.SetPath{path: [:iteration], value: 5}

# `set_iteration_status`

```elixir
@spec set_iteration_status(atom()) :: Jido.Agent.StateOp.SetPath.t()
```

Creates a StateOp to set the iteration status.

## Examples

    iex> Helpers.set_iteration_status(:awaiting_llm)
    %Jido.Agent.StateOp.SetPath{path: [:status], value: :awaiting_llm}

# `set_pending_tools`

```elixir
@spec set_pending_tools([map()]) :: Jido.Agent.StateOp.SetState.t()
```

Creates a StateOp to set pending tool calls.

## Examples

    iex> tools = [%{id: "call_1", name: "search", arguments: %{query: "test"}}]
    iex> Helpers.set_pending_tools(tools)
    %Jido.Agent.StateOp.SetState{attrs: %{pending_tool_calls: tools}}

# `set_strategy_field`

```elixir
@spec set_strategy_field(atom(), term()) :: Jido.Agent.StateOp.SetPath.t()
```

Creates a StateOp to set a specific field in the strategy state.

## Examples

    iex> Helpers.set_strategy_field(:status, :running)
    %Jido.Agent.StateOp.SetPath{path: [:status], value: :running}

# `set_streaming_text`

```elixir
@spec set_streaming_text(String.t()) :: Jido.Agent.StateOp.SetPath.t()
```

Creates a StateOp to set the streaming text.

## Examples

    iex> Helpers.set_streaming_text("Hello")
    %Jido.Agent.StateOp.SetPath{path: [:streaming_text], value: "Hello"}

# `set_termination_reason`

```elixir
@spec set_termination_reason(atom()) :: Jido.Agent.StateOp.SetPath.t()
```

Creates a StateOp to set the termination reason.

## Examples

    iex> Helpers.set_termination_reason(:final_answer)
    %Jido.Agent.StateOp.SetPath{path: [:termination_reason], value: :final_answer}

# `set_usage`

```elixir
@spec set_usage(map()) :: Jido.Agent.StateOp.SetState.t()
```

Creates a StateOp to set the usage metadata.

## Examples

    iex> usage = %{input_tokens: 10, output_tokens: 20}
    iex> Helpers.set_usage(usage)
    %Jido.Agent.StateOp.SetState{attrs: %{usage: usage}}

# `update_config`

```elixir
@spec update_config(map()) :: Jido.Agent.StateOp.SetState.t()
```

Creates a StateOp to update the config field in strategy state.

## Examples

    iex> config = %{tools: [], model: "test"}
    iex> Helpers.update_config(config)
    %Jido.Agent.StateOp.SetState{attrs: %{config: config}}

# `update_config_fields`

```elixir
@spec update_config_fields(map()) :: [Jido.Agent.StateOp.SetPath.t()]
```

Creates StateOps to update multiple config fields at once.

## Examples

    iex> ops = Helpers.update_config_fields(%{tools: [], model: "test"})
    iex> length(ops)
    2
    iex> hd(ops).path
    [:config, :tools]

# `update_strategy_state`

```elixir
@spec update_strategy_state(map()) :: Jido.Agent.StateOp.SetState.t()
```

Creates a StateOp to update the strategy state.

Performs a deep merge of the given attributes into the strategy state.

## Examples

    iex> Helpers.update_strategy_state(%{status: :running, iteration: 1})
    %Jido.Agent.StateOp.SetState{attrs: %{status: :running, iteration: 1}}

# `update_tools_config`

```elixir
@spec update_tools_config([module()], %{required(String.t()) =&gt; module()}, [map()]) ::
  [
    Jido.Agent.StateOp.SetPath.t()
  ]
```

Creates StateOps to update tools, actions_by_name, and reqllm_tools together.

This is a common pattern when registering/unregistering tools.

## Examples

    iex> tools = [SomeAction]
    iex> actions_by_name = %{"action" => SomeAction}
    iex> reqllm_tools = [%{name: "action"}]
    iex> ops = Helpers.update_tools_config(tools, actions_by_name, reqllm_tools)
    iex> length(ops)
    3
    iex> hd(ops).path
    [:config, :tools]

---

*Consult [api-reference.md](api-reference.md) for complete listing*
