Pipe-friendly building blocks for composing custom execution modes.
Every step function follows the pipeline convention:
- Input/output:
{:continue, chain}means keep processing - Any other tuple (
:ok,:pause,:error) is a terminal — passed through unchanged - Step functions take the pipeline result as first arg, optional config as second
Example: Custom Mode
defmodule MyApp.Modes.Simple do
@behaviour LangChain.Chains.LLMChain.Mode
import LangChain.Chains.LLMChain.Mode.Steps
@impl true
def run(chain, opts) do
chain = ensure_mode_state(chain)
{:continue, chain}
|> call_llm()
|> execute_tools()
|> check_max_runs(opts)
|> continue_or_done(&run/2, opts)
end
end
Summary
Functions
Call the LLM (single step). Wraps LLMChain.execute_step/1.
Check if max runs have been exceeded.
Check if execution should pause (e.g., node draining).
Check if a target tool was called in the most recent tool results.
Decide whether to loop or return.
Initialize mode_state in custom_context if not already present.
Execute pending tool calls. Wraps LLMChain.execute_tool_calls/1.
Get the current run count from mode_state.
Types
@type pipeline_result() :: {:continue, LangChain.Chains.LLMChain.t()} | {:ok, LangChain.Chains.LLMChain.t()} | {:ok, LangChain.Chains.LLMChain.t(), term()} | {:pause, LangChain.Chains.LLMChain.t()} | {:error, LangChain.Chains.LLMChain.t(), term()} | {:interrupt, LangChain.Chains.LLMChain.t(), term()}
Functions
Call the LLM (single step). Wraps LLMChain.execute_step/1.
On success, increments mode_state.run_count in custom_context.
Check if max runs have been exceeded.
Reads run_count from custom_context.mode_state and compares against
:max_runs in opts (default: 25).
Check if execution should pause (e.g., node draining).
Reads :should_pause? from opts — a zero-arity function that returns boolean.
Check if a target tool was called in the most recent tool results.
Reads :tool_names from opts — a list of tool name strings.
If a matching tool result is found, returns {:ok, chain, tool_result}.
Decide whether to loop or return.
{:continue, chain}withneeds_response: true→ callrun_fn.(chain, opts)(loop){:continue, chain}withneeds_response: false→{:ok, chain}(done)- Any terminal result → pass through as-is
Initialize mode_state in custom_context if not already present.
Call this at the top of your mode's run/2 to set up run_count tracking.
On the first call, creates mode_state: %{run_count: 0}.
On recursive calls (mode_state already exists), returns chain unchanged.
Execute pending tool calls. Wraps LLMChain.execute_tool_calls/1.
Get the current run count from mode_state.