Jido.AI.Reasoning.ChainOfThought.Machine (Jido AI v2.1.0)

View Source

Pure state machine for the Chain-of-Thought (CoT) reasoning pattern.

This module implements state transitions for a CoT agent without any side effects. It uses Fsmx for state machine management and returns directives that describe what external effects should be performed.

Overview

Chain-of-Thought prompting encourages LLMs to break down complex problems into intermediate steps before providing a final answer. This leads to better reasoning on multi-step problems like math, logic, and common sense reasoning.

States

  • :idle - Initial state, waiting for a prompt
  • :reasoning - Waiting for LLM response with reasoning
  • :completed - Final state, reasoning complete
  • :error - Error state

Usage

The machine is used by the CoT strategy:

machine = Machine.new()
{machine, directives} = Machine.update(machine, {:start, prompt, call_id}, env)

All state transitions are pure - side effects are described in directives.

Status Type Boundary

Internal (Machine struct): Status is stored as strings ("idle", "completed") due to Fsmx library requirements.

External (Strategy state, Snapshots): Status is converted to atoms (:idle, :completed) via to_map/1 before storage in agent state.

Never compare machine.status directly with atoms - use Machine.to_map/1 first.

Summary

Types

External status (atom) - used in strategy state after to_map/1 conversion

Internal machine status (string) - required by Fsmx library

t()

Functions

Returns the default CoT system prompt.

Extracts reasoning steps and conclusion from LLM response text.

Creates a machine from a map (e.g., from strategy state storage).

Generates a unique call ID for LLM requests.

Creates a new machine in the idle state.

Converts the machine state to a map suitable for strategy state storage.

Updates the machine state based on a message.

Types

directive()

@type directive() :: {:call_llm_stream, id :: String.t(), context :: list()}

external_status()

@type external_status() :: :idle | :reasoning | :completed | :error

External status (atom) - used in strategy state after to_map/1 conversion

internal_status()

@type internal_status() :: String.t()

Internal machine status (string) - required by Fsmx library

msg()

@type msg() ::
  {:start, prompt :: String.t(), call_id :: String.t()}
  | {:llm_result, call_id :: String.t(), result :: term()}
  | {:llm_partial, call_id :: String.t(), delta :: String.t(),
     chunk_type :: atom()}

step()

@type step() :: %{number: pos_integer(), content: String.t()}

t()

@type t() :: %Jido.AI.Reasoning.ChainOfThought.Machine{
  conclusion: String.t() | nil,
  current_call_id: String.t() | nil,
  prompt: String.t() | nil,
  raw_response: String.t() | nil,
  result: term(),
  started_at: integer() | nil,
  status: internal_status(),
  steps: [step()],
  streaming_text: String.t(),
  termination_reason: termination_reason(),
  usage: usage()
}

termination_reason()

@type termination_reason() :: :success | :error | nil

usage()

@type usage() :: %{
  optional(:input_tokens) => non_neg_integer(),
  optional(:output_tokens) => non_neg_integer(),
  optional(:total_tokens) => non_neg_integer()
}

Functions

before_transition(struct, from, to, state_field)

default_system_prompt()

@spec default_system_prompt() :: String.t()

Returns the default CoT system prompt.

extract_steps_and_conclusion(text)

@spec extract_steps_and_conclusion(String.t()) :: {[step()], String.t() | nil}

Extracts reasoning steps and conclusion from LLM response text.

Supports multiple formats:

  • Numbered steps: "Step 1:", "1.", "1)"
  • Bullet points: "- ", "* ", "• "
  • Conclusion markers: "Conclusion:", "Answer:", "Therefore:", "Final Answer:"

Returns {steps, conclusion} where steps is a list of step maps and conclusion is the final answer string or nil.

from_map(map)

@spec from_map(map()) :: t()

Creates a machine from a map (e.g., from strategy state storage).

generate_call_id()

@spec generate_call_id() :: String.t()

Generates a unique call ID for LLM requests.

new()

@spec new() :: t()

Creates a new machine in the idle state.

to_map(machine)

@spec to_map(t()) :: map()

Converts the machine state to a map suitable for strategy state storage.

update(machine, msg, env \\ %{})

@spec update(t(), msg(), map()) :: {t(), [directive()]}

Updates the machine state based on a message.

Returns the updated machine and a list of directives describing external effects to be performed.

Messages

  • {:start, prompt, call_id} - Start CoT reasoning
  • {:llm_result, call_id, result} - Handle LLM response
  • {:llm_partial, call_id, delta, chunk_type} - Handle streaming chunk

Directives

  • {:call_llm_stream, id, context} - Request LLM call with CoT prompt