Skip to main content
ReasonBlocks records two parallel views of an agent run: an internal TraceState populated with StepRecord dataclasses (used by the FSM and retrieval pipeline), and a user-facing step_log of StepLogEntry objects exposed on the middleware. Most consumers read the step_log; TraceState and StepRecord are documented here for completeness and for callers building custom integrations.
from reasonblocks.types import StepRecord, TraceState, FSMState
from reasonblocks.middleware import StepLogEntry

StepLogEntry

StepLogEntry is the type you actually iterate after a run. The middleware appends one entry per wrap_model_call (i.e. per LLM call). Access via middleware.step_log.
step
int
Zero-based step index. Monotonically increasing across the run.
timestamp
float
Wall-clock time (time.time()) when the entry was created in before_model.
difficulty
float | None
Difficulty score for this step, or None for the very first call (no thought to score yet).
fsm_state
str
String value of the FSM state (e.g. "INIT", "FAST", "NORMAL", "SLOW", "SKIP").
model_id
str
Resolved model identifier used for this call after FSM-based routing. Empty string when no override applied.
injections
list[str]
Short previews (≤150 chars) of each injection appended to the system prompt for this call.
injection_sources
list[str]
Source class names of the injections (e.g. "E1Injection", "MonitorSteeringInjection").
intervention_texts
list[str]
Full, untruncated injection text for each injection, in the same order as injection_sources.
monitors_fired
list[str]
Specific monitor names that fired on this step (e.g. "loop_detector", "hedge").
failure_type
str | None
Categorical failure label from the server-side monitor evaluation, when present.
skipped_reason
str | None
Set when retrieval was skipped — e.g. "FSM=FAST, e-traces skipped". None otherwise.
thought_preview
str
First 120 characters of the agent’s thought for this step, or "(first call)" when there is no thought yet.
tool_calls
list[str]
Names of tool calls produced by the LLM for this step.
tokens
int
Total tokens for this call (extracted from the AIMessage usage_metadata).
latency_ms
float
Milliseconds elapsed since the previous entry’s timestamp. 0.0 for the first entry.

as_dict()

StepLogEntry.as_dict() returns a serialisable dict with every field above (rounded as appropriate). Optional fields like difficulty, model_id, skipped_reason, and failure_type are only included when they are populated.
import json

with rb.middleware(...) as mw:
    result = agent.invoke({"messages": [...]})

print(json.dumps([e.as_dict() for e in mw.step_log], indent=2))

StepRecord

StepRecord is the internal per-step dataclass written into TraceState.steps. The FSM and retrieval pipeline read from it; callers typically use StepLogEntry instead.
step_index
int
required
Zero-based position of this step in the trace.
thought
str
required
The agent’s reasoning text for this step (extracted from the latest AIMessage).
action
str | None
Tool name selected by the agent for this step, or None if no tool was called.
action_input
str | None
String form of the tool arguments, or None when action is None.
observation
str | None
Most recent tool message content (truncated to 400 chars), or None if no observation.
difficulty
float
Difficulty score for this step in [0.0, 1.0]. Defaults to 0.0 until scored.
state
FSMState
FSM state recorded after scoring this step. Defaults to FSMState.INIT.
tokens_used
int
Tokens consumed by the LLM call that produced this step. Updated post-call from usage_metadata.

TraceState

TraceState is the run-level accumulator owned by TraceStateManager. The middleware reads from it during retrieval; consumers do not normally touch it directly.
trace_id
str
required
Unique identifier for this run.
steps
list[StepRecord]
Ordered list of every StepRecord recorded so far.
current_state
FSMState
The FSM state at the time of inspection. Starts as FSMState.INIT.
difficulty_history
list[float]
Flat list of difficulty scores in step order. Equivalent to [s.difficulty for s in steps].
total_tokens
int
Sum of StepRecord.tokens_used across all steps.
token_budget
int | None
The token_budget configured on the parent ReasonBlocks instance, or None when no budget was set.

Reading step_log after a run

from reasonblocks import ReasonBlocks

rb = ReasonBlocks(api_key="rb_live_...")

with rb.middleware(run_id="run-1", agent_name="reviewer", task="review PR #42") as mw:
    result = agent.invoke({"messages": [("user", "Review the changes in PR #42")]})

for entry in mw.step_log:
    print(
        f"  [{entry.step}] difficulty={entry.difficulty}  "
        f"state={entry.fsm_state}  tools={entry.tool_calls}"
    )
entry.as_dict() returns a serialisable view useful for logging or exporting run data without writing your own field projection.