Skip to main content
CodebaseMemory is a thin client over the rb-api /findings/... endpoints. It lets an agent persist observations during a run — bug locations, behavioral notes, architectural patterns — and recall them semantically on future runs. All transport errors are caught and surface as empty results / None / 0, so a missing rb-api never breaks the agent loop.

Initialize

from reasonblocks import CodebaseMemory

memory = CodebaseMemory(
    codebase_id="pydantic/pydantic",   # any stable identifier for the repo
    api_key="rb_live_...",
    base_url=None,                      # defaults to the hosted API
    timeout=None,                       # defaults to the SDK timeout
)
Constructor signature:
codebase_id
str
required
Required. Identifier the findings are scoped under. Common patterns: org/repo, repo@sha:abc123. All findings stored under the same id are searchable together.
api_key
str
required
Sent as Authorization: Bearer <api_key> on every request.
base_url
str
default:"DEFAULT_BASE_URL"
Override to point at a self-hosted rb-api. Trailing slashes are stripped.
timeout
float
default:"DEFAULT_TIMEOUT"
HTTP timeout passed to the underlying httpx.Client.
Pass the same codebase_id to CodebaseMemory and to rb.middleware(codebase_id=...) if you also use the middleware — the middleware uses it to scope E1 retrieval.

Store and recall

Store a finding

finding_id = memory.store(
    content="BaseModel.validate raises ValidationError on extra=forbid when unknown fields are present",
    file_path="pydantic/main.py",
    finding_type="behavior",  # bug | behavior | pattern | note
    metadata={"reviewer": "alice", "severity": "low"},
)
Returns the finding id on success or None on transport error. Content is truncated server-side at 8000 chars; file_path at 512; finding_type at 64.

Recall by query

results = memory.recall(
    "validator error handling",
    top_k=5,
    score_threshold=0.25,
)

for r in results:
    print(r["file_path"], r["score"], r["content"])
Returns a list of {content, file_path, finding_type, score, ...} dicts ordered by descending score. Raise score_threshold for tighter matches; lower it for more recall.

Pre-formatted recall (for tool outputs)

format_recall combines recall with a human-readable renderer. Use it when a tool needs to return findings as a string.
digest = memory.format_recall(
    "validator error handling",
    top_k=5,
    score_threshold=0.25,
    max_len=300,    # truncate each finding's content
)
print(digest)
# Found 2 relevant prior findings:
#
# --- [behavior] pydantic/main.py  (relevance 0.82) ---
# BaseModel.validate raises ValidationError on extra=forbid ...
Returns "(no relevant findings in cache yet)" when nothing matches.

Other operations

List every finding

all_findings = memory.list_all()
Useful for auditing the cache before a run.

Batch store

count = memory.store_many([
    {"content": "...", "file_path": "pydantic/main.py", "finding_type": "bug"},
    {"content": "...", "file_path": "pydantic/validators.py", "finding_type": "behavior"},
])
Each dict needs at least content. file_path, finding_type, and metadata are optional. Returns the count actually stored (entries that fail or have empty content are skipped).

Coverage check

coverage_for returns the fraction of a file list that has at least one stored finding. A soft suffix-match means a stored pydantic/main.py covers a query for bare main.py.
files = ["pydantic/main.py", "pydantic/validators.py", "pydantic/fields.py"]
coverage = memory.coverage_for(files)

if coverage < 0.5:
    print("cache is sparse - do a full review")
else:
    print(f"cache covers {coverage:.0%} of files")

Invalidate stale findings

When files change, drop their findings:
deleted = memory.invalidate(["pydantic/main.py", "pydantic/validators.py"])
Returns the number deleted server-side.

Clear the entire cache

memory.clear()  # drops every finding for the codebase_id - irreversible
clear() cannot be undone. Use invalidate() for targeted cleanups.

Release the HTTP client

memory.close()
CodebaseMemory holds a persistent httpx.Client to keep TCP connections warm across calls. Call close() when you’re done if your process needs to release sockets cleanly.

Pair with ImportGraph for blast-radius invalidation

When a file changes, you usually want to invalidate every file that imports it. ImportGraph (see Import graph) gives you the blast radius:
from reasonblocks import ImportGraph

graph = ImportGraph()
graph.build_from_files({path: open(path).read() for path in py_files})

changed_files = ["pydantic/main.py"]
affected = graph.blast_radius(changed_files, depth=1)

deleted = memory.invalidate(list(affected))
print(f"invalidated {deleted} findings across {len(affected)} files")
ImportGraph.build_from_files requires networkx. Install with pip install networkx.

Wire into your agent

Each framework has a tool factory wrapping CodebaseMemory (and optionally ImportGraph). See:
  • LangChain tools via make_langchain_tools
  • OpenAI Agents tools via make_openai_tools
  • reasonblocks.integrations.claude_tools.make_claude_tools for the Anthropic Messages API