Skip to content

Append 20,000 turns

For the complete documentation index see: llms.txt

All documentation pages available in markdown.

The demo script is adapted from the infinite chat history example in the adk-aerospike repository. Open that source if you want to see how each turn is constructed before you run it yourself.

The script appends 20,000 alternating user and assistant turns with scoped state. Each append clears in-memory events so the session object stays small.

Save and run the demo script

  1. Create infinite-chat-history-demo.py with the script in this section (or copy from the adk-aerospike example):

    import asyncio
    import time
    import aerospike
    from aerospike_helpers.operations import map_operations
    from google.adk.events import Event, EventActions
    from google.adk.sessions.base_session_service import GetSessionConfig
    from google.genai import types as genai_types
    from adk_aerospike import AerospikeSessionService
    APP, USER, SID = "demo-app", "user-1", "session-1"
    TURNS = 20_000
    EVENTS_BIN = "events"
    def summarize_segments(
    client: aerospike.Client, cur: int
    ) -> tuple[int, list[int]]:
    counts: list[int] = []
    for gidx in range(cur + 1):
    seg_pk = ("test", "adk_sessions", f"{APP}:{USER}:{SID}:g:{gidx:08d}")
    try:
    _, _, res = client.operate(
    seg_pk, [map_operations.map_size(EVENTS_BIN)]
    )
    n = int(res.get(EVENTS_BIN, 0))
    except Exception:
    n = 0
    if n:
    counts.append(n)
    return len(counts), counts
    async def main() -> None:
    client = aerospike.client(
    {
    "hosts": [("127.0.0.1", 3000)],
    "max_error_rate": 100,
    "error_rate_window": 1,
    }
    ).connect()
    session_service = AerospikeSessionService(client, "test")
    session = await session_service.create_session(
    app_name=APP, user_id=USER, session_id=SID
    )
    t0 = time.time()
    started = time.perf_counter()
    for i in range(TURNS):
    author = "user" if i % 2 == 0 else "assistant"
    await session_service.append_event(
    session,
    Event(
    invocation_id=f"turn-{i:05d}",
    author=author,
    timestamp=t0 + i * 0.001,
    content=genai_types.Content(
    role="user" if author == "user" else "model",
    parts=[
    genai_types.Part(
    text=(
    f"turn {i} from {author}: "
    "context line for a long-running agent session. "
    )
    )
    ],
    ),
    actions=EventActions(
    state_delta={
    "step": i,
    "app:tenant": "acme",
    "user:locale": "en-US",
    }
    ),
    ),
    )
    session.events.clear()
    if i and i % 5000 == 0:
    print(f" appended {i}...")
    print(f"append_event x{TURNS} in {time.perf_counter() - started:.1f}s")
    full = await session_service.get_session(
    app_name=APP, user_id=USER, session_id=SID
    )
    print(f"full history: {len(full.events)} events")
    print(f" first: {full.events[0].content.parts[0].text}") # type: ignore[union-attr]
    print(f" last: {full.events[-1].content.parts[0].text}") # type: ignore[union-attr]
    print(f" merged state keys: {list(full.state.keys())}")
    recent5 = await session_service.get_session(
    app_name=APP,
    user_id=USER,
    session_id=SID,
    config=GetSessionConfig(num_recent_events=5),
    )
    texts5 = [e.content.parts[0].text for e in recent5.events] # type: ignore[union-attr]
    print(f"num_recent_events=5: {texts5}")
    after = await session_service.get_session(
    app_name=APP,
    user_id=USER,
    session_id=SID,
    config=GetSessionConfig(after_timestamp=t0 + 19_990 * 0.001),
    )
    print(f"after_timestamp (last ~10 turns): {len(after.events)} events")
    pk = ("test", "adk_sessions", f"{APP}:{USER}:{SID}")
    _, _, sbins = client.select(pk, ["cur", "state"])
    cur = int(sbins.get("cur", 0))
    n_segs, sizes = summarize_segments(client, cur)
    print(
    f"session record — cur={cur}, segments={n_segs}, "
    f"events_in_segments={sum(sizes)}, "
    f"session_state_keys={len(sbins.get('state') or {})}"
    )
    if sizes:
    print(f" segment sizes (first 3): {sizes[:3]} ... (last 3): {sizes[-3:]}")
    listed = await session_service.list_sessions(app_name=APP, user_id=USER)
    print(f"list_sessions: {[s.id for s in listed.sessions]}")
    await session_service.delete_session(app_name=APP, user_id=USER, session_id=SID)
    session_service.close()
    print("Connection closed.")
    if __name__ == "__main__":
    asyncio.run(main())
  2. Run the script:

    Terminal window
    python infinite-chat-history-demo.py
    Expected result
    appended 5000...
    appended 10000...
    appended 15000...
    append_event x20000 in 13.9s

    The append phase prints progress every 5,000 turns. On a typical laptop, allow about 15 seconds for the demo to complete 20,000 appends.

    Match those four lines in your terminal. The last line (append_event x20000 in …) is the summary. x20000 confirms all appends finished. The seconds after in vary by hardware.

    The script continues into read and inspection phases after these lines. Let it run to completion for the remaining pages.

Review the append phase output

  1. Read the append loop:

    Each turn alternates user and assistant authors. Every append_event carries a multi-scope state_delta, then clears in-memory events so the session object stays small across 20,000 turns:

    infinite-chat-history-demo.py
    for i in range(TURNS):
    author = "user" if i % 2 == 0 else "assistant"
    await session_service.append_event(
    session,
    Event(
    invocation_id=f"turn-{i:05d}",
    author=author,
    timestamp=t0 + i * 0.001,
    content=genai_types.Content(
    role="user" if author == "user" else "model",
    parts=[
    genai_types.Part(
    text=(
    f"turn {i} from {author}: "
    "context line for a long-running agent session. "
    )
    ],
    ],
    ),
    actions=EventActions(
    state_delta={
    "step": i, # session-scoped
    "app:tenant": "acme", # app-scoped
    "user:locale": "en-US", # user-scoped
    }
    ),
    ),
    )
    session.events.clear()
    • step updates on every turn and stays session-scoped.
    • app:tenant and user:locale route to app-wide and user-wide Aerospike records.
    • session.events.clear() drops cached events after each persist. Storage holds the full history. The in-memory session does not grow with every turn.
  2. Read the progress lines:

    Inside the loop, the script prints every 5,000 turns and summarizes when the loop finishes:

    infinite-chat-history-demo.py
    if i and i % 5000 == 0:
    print(f" appended {i}...")
    # after the loop:
    print(f"append_event x{TURNS} in {time.perf_counter() - started:.1f}s")
    appended 5000...
    appended 10000...
    appended 15000...
    append_event x20000 in 13.9s

    Events pack into map segments until Aerospike signals a segment is full, then rollover opens the next segment. Segment counts are not printed during the append phase. You verify them on Inspect segments after the read output.

Feedback

Was this page helpful?

What type of feedback are you giving?

What would you like us to know?

+Capture screenshot

Can we reach out to you?