Skip to main content
rb.middleware() returns a ReasonBlocksMiddleware — a langchain.agents.middleware.AgentMiddleware subclass that also acts as a context manager. You attach it to create_agent like any other middleware. It hooks before_agent, before_model, wrap_model_call, and after_agent to score each step, evaluate trajectory monitors server-side, inject steering text into the system message, and (optionally) route the model based on the FSM state.
1

Install

pip install reasonblocks "langchain>=1.0" langchain-anthropic langgraph
2

Initialize ReasonBlocks

Create one ReasonBlocks per process and call rb.middleware() per run. Each middleware instance is single-use.
import os
from reasonblocks import ReasonBlocks

rb = ReasonBlocks(
    api_key=os.environ["REASONBLOCKS_API_KEY"],
    base_url=os.environ.get("REASONBLOCKS_BASE_URL"),  # omit for the hosted API
)
The constructor does not auto-read REASONBLOCKS_API_KEY — your code must pass it explicitly. The base_url default is read from REASONBLOCKS_BASE_URL at import time by reasonblocks._settings; if you set that env var before importing reasonblocks, you can omit base_url here.
3

Wrap your agent in the context manager

The recommended pattern is with mw:. The context manager guarantees that the run_finish telemetry event fires on success, exception, or explicit failure marking.
from langchain.agents import create_agent

mw = rb.middleware(
    agent_name="bugfixer",
    task="fix the TypeError in handler.py",
    framework="langchain",
    model="anthropic:claude-haiku-4-5-20251001",
    codebase_id="my-org/my-repo",
)

agent = create_agent(
    model="anthropic:claude-haiku-4-5-20251001",
    tools=[search_codebase, read_file, edit_file, run_tests],
    system_prompt="You are a senior software engineer. Fix bugs by searching, reading, editing, and testing.",
    middleware=[mw],
)

with mw:
    result = agent.invoke(
        {"messages": [("user", "There's a TypeError in the request handler. Find and fix it.")]},
    )
4

Tag runs for the dashboard

Every parameter on rb.middleware() is optional. Anything not consumed by a named field rides along in metadata on the run row.
mw = rb.middleware(
    run_id="pr-42-attempt-3",                    # auto-generated UUID if omitted
    agent_name="bugfixer",
    task="fix the TypeError in handler.py",
    framework="langchain",                        # default
    model="anthropic:claude-haiku-4-5-20251001",  # display only
    codebase_id="my-org/my-repo",
    org_id="6d3f...",                             # "default" if omitted
    project_id="a91b...",                         # "default" if omitted
    metadata={"pr_number": 42, "experiment": "v2"},
)
When your api_key is a per-org rb_live_* key, the API overrides org_id / project_id with the key’s authoritative scope. Leaving them at "default" is fine.
5

Mark explicit failures

If the agent returns normally but the run was logically a failure (wrong answer, tests still failing), call mark_failure(reason=...) before the with block exits. Exceptions that escape the block are recorded automatically as failure: <ExceptionType>.
with mw:
    result = agent.invoke({"messages": [("user", "Fix the bug.")]})
    if not tests_pass(result):
        mw.mark_failure(reason="tests_still_failing")
6

Inspect step_log

mw.step_log is a list[StepLogEntry] populated as the agent runs. Each entry holds the FSM state, difficulty, resolved model id (when routed), monitors that fired, full intervention text, tool calls, tokens, and latency.
for entry in mw.step_log:
    print(
        f"step {entry.step}: state={entry.fsm_state} "
        f"difficulty={entry.difficulty} model_id={entry.model_id} "
        f"tokens={entry.tokens}"
    )
    if entry.monitors_fired:
        print(f"  monitors fired: {entry.monitors_fired}")
    for src, text in zip(entry.injection_sources, entry.intervention_texts):
        print(f"  [{src}] {text[:160]}")
Call entry.as_dict() for a JSON-shaped view.

Add CodebaseMemory tools

make_langchain_tools wraps a CodebaseMemory (and optionally an ImportGraph) into @tool-decorated callables. The agent uses them to recall prior findings before re-reading files and to persist new findings during a run.
from reasonblocks import CodebaseMemory, ImportGraph
from reasonblocks.integrations.langchain_tools import make_langchain_tools

memory = CodebaseMemory(
    codebase_id="my-org/my-repo",
    api_key=os.environ["REASONBLOCKS_API_KEY"],
)
graph = ImportGraph().build_from_files({path: open(path).read() for path in py_files})

rb_tools = make_langchain_tools(
    memory,
    graph,
    recall_top_k=5,
    recall_threshold=0.25,
    enable_recall=True,
    enable_store=True,
    enable_impact=True,
)

agent = create_agent(
    model="anthropic:claude-haiku-4-5-20251001",
    tools=[*rb_tools, search_codebase, read_file, edit_file, run_tests],
    system_prompt="Call recall_findings before reading any file.",
    middleware=[mw],
)
The factory adds up to three tools:
  • recall_findings — semantic search over prior findings.
  • store_finding — persist a new finding for future runs (hidden when enable_store=False).
  • impact_analysis — blast-radius lookup via ImportGraph (only added when a graph is passed).
ImportGraph.build_from_files requires networkx. Install with pip install networkx.
See Persist agent findings with CodebaseMemory for the underlying API.

Complete example

import os
from langchain.agents import create_agent
from langchain_core.tools import tool
from reasonblocks import CodebaseMemory, ReasonBlocks
from reasonblocks.integrations.langchain_tools import make_langchain_tools

rb = ReasonBlocks(api_key=os.environ["REASONBLOCKS_API_KEY"])
memory = CodebaseMemory(
    codebase_id="my-org/my-repo",
    api_key=os.environ["REASONBLOCKS_API_KEY"],
)
rb_tools = make_langchain_tools(memory)

@tool
def search_codebase(query: str) -> str:
    """Search the codebase for files matching a query."""
    ...

@tool
def read_file(path: str) -> str:
    """Read the contents of a file."""
    ...

mw = rb.middleware(
    agent_name="bugfixer",
    task="fix the TypeError in handler.py",
    codebase_id="my-org/my-repo",
)

agent = create_agent(
    model="anthropic:claude-haiku-4-5-20251001",
    tools=[*rb_tools, search_codebase, read_file],
    system_prompt="You are a senior software engineer. Fix bugs methodically.",
    middleware=[mw],
)

with mw:
    result = agent.invoke({"messages": [("user", "Fix the bug.")]})
Each mw is single-use — call rb.middleware(...) again for the next run.