Brainby arc-labs/docs
Python SDK

Pagination

Streamed frames and keyset cursors — how BrainClient's list and export verbs page, and the *_frames methods that expose cursors.

Pagination is a BrainClient (wire) concern. The HTTP client's recall takes a max_results cap and returns a single RecallResult.

Two forms per verb

Every streamed verb exposes both:

OptionTypeDefault / Env
flattening forme.g. memory_list(req) → list[...]

Drains every streamed frame and concatenates their items into one list. Convenient when the result fits in memory.

frames forme.g. memory_list_frames(req) → list[...Frame]

Returns each decoded response frame as streamed, preserving next_cursor, cumulative_count, and is_final. Use it to resume across pages.

Verbs with both forms include recall / recall_frames, memory_list / memory_list_frames, graph_fetch / graph_fetch_frames, statement_history, list_entities, list_statements, list_relations_from, list_relations_to, list_schemas, traverse_relations, plan, and reason.

Keyset cursors

MEMORY_LIST and GRAPH_FETCH page with an opaque keyset cursor. Send an empty cursor (b"") on the first page; each frame's next_cursor resumes the next. An empty next_cursor on the final frame means "exhausted"; a non-empty one means "more pages available, resume with this".

from brain_db_sdk.wire.types import (
    MemoryListRequest, MemoryListSort, MemoryListDir, MemoryListTimeAxis,
)

def all_memories(client):
    cursor = b""
    while True:
        req = MemoryListRequest(
            sort=MemoryListSort.CREATED,
            dir=MemoryListDir.DESC,
            limit=100,              # validated server-side to 1..=100
            cursor=cursor,
            kinds=[],               # empty = all kinds
            include_tombstoned=False,
            time_axis=MemoryListTimeAxis.CREATED,
            from_unix_nanos=0,      # 0 = no lower bound
            to_unix_nanos=0,        # 0 = no upper bound
            salience_min=0.0,
            salience_max=1.0,
            text_contains="",       # empty = no filter
        )
        frames = client.memory_list_frames(req)
        for frame in frames:
            yield from frame.items
        last = frames[-1]
        if last.is_final and not last.next_cursor:
            break
        cursor = last.next_cursor

When the whole result fits in memory, the flattening form is simpler — it drains every frame for you:

items = client.memory_list(req)   # list[MemoryListItem]

Graph export

graph_fetch pages the same way, over an opaque cursor (limit validated to 1..=500). Nodes and edges may repeat across pages — the export favors completeness over disjointness — so dedup by id if you accumulate them.

from brain_db_sdk.wire.types import GraphFetchRequest

def full_graph(client):
    cursor = b""
    nodes, edges = {}, {}
    while True:
        req = GraphFetchRequest(
            limit=500,
            cursor=cursor,
            include_statements=True,
            include_memories=False,
            include_tombstoned=False,
        )
        frames = client.graph_fetch_frames(req)
        for frame in frames:
            for n in frame.nodes:
                nodes[n.id] = n                       # dedup by node id
            for e in frame.edges:
                edges[(e.from_id, e.to_id, e.kind)] = e   # dedup by endpoints + kind
        last = frames[-1]
        if last.is_final and not last.next_cursor:
            break
        cursor = last.next_cursor
    return list(nodes.values()), list(edges.values())

Streamed recall

recall streams too, but its frames carry no cursor — they terminate at EOS. Use recall_frames to see each frame's cumulative counts and estimated_remaining; the flattening recall concatenates them into one RecallAnswer. See Recall.

Was this page helpful?

On this page