> ## Documentation Index
> Fetch the complete documentation index at: https://docs.anonalabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# LlamaIndex

> Long-term memory blocks for LlamaIndex agents.

LlamaIndex's `Memory` composes blocks. Anona plugs in as one of them, supplying
long-term memory alongside the short-term buffer rather than replacing it. No
vector store to operate, no LLM key to supply.

```bash theme={null}
pip install 'anona[llamaindex]'
```

## Setup

```python theme={null}
from llama_index.core.memory import Memory
from anona.integrations import MemoryBridge
from anona.integrations.llamaindex import AnonaMemoryBlock

bridge = MemoryBridge(api_key="anona_live_...", space_id="assistant")

memory = Memory.from_defaults(
    session_id="chat-1",
    # A real database, not the in-memory default, see "Use a durable
    # database" below.
    async_database_uri="sqlite+aiosqlite:////path/to/chat.db",
    memory_blocks=[AnonaMemoryBlock(bridge=bridge)],
)
```

Pass `memory` to any LlamaIndex agent (`FunctionAgent`, `ReActAgent`, ...).
`Memory` calls this block's context into its own template and prepends it to
the system message on every model call, so relevant memories are visible
before the agent's first response of a turn, including before each
follow-up model call inside a tool-calling round, not just the first one.
LlamaIndex re-asks for the same query several times over the course of one
turn; this block keeps a short-lived cache so those repeats reuse the first
answer instead of calling Anona again each time.

## How and when a turn is stored

Unlike Anona's LangChain or CrewAI adapters, this block does not store a turn
the moment it finishes. LlamaIndex's `Memory` keeps its own short-term FIFO of
recent messages (`token_limit` / `chat_history_token_ratio`, 30,000 tokens /
70% by default) and only pushes the oldest messages out to each memory
block, Anona's included, once that buffer fills up. That is the same
contract LlamaIndex's own built-in blocks (`VectorMemoryBlock`,
`FactExtractionMemoryBlock`) are written against: a memory block is long-term
storage for what falls out of the short-term window, not a mirror of every
turn.

In practice:

* A short conversation may never cross the default \~21,000-token threshold,
  in which case nothing reaches Anona at all. The whole conversation still
  lives in LlamaIndex's own short-term buffer for the life of the session.
* When the buffer does flush, whole turns move together. A tool-calling
  round (the question, the tool call, the tool result, the final answer),
  arrives as one batch, never split across separate writes.
* **The most recent turn is never flushed by itself, at any `token_limit`.**
  `Memory`'s eviction always keeps at least one complete turn resident, so
  the newest turn only leaves the buffer once *another* turn pushes it out.
  `Memory` has no flush-now or close method. Concretely: **the last turn of
  a conversation is never stored by this block at all**, no matter how low
  you set `token_limit`. There is always one more turn that never arrives
  to evict it.

Lowering `token_limit` and/or `chat_history_token_ratio` on
`Memory.from_defaults(...)` makes every *earlier* turn reach Anona sooner.
It does not change the point above. The last turn is still excluded either
way.

### Guaranteeing the last turn is captured

If losing the final exchange of a conversation is a problem for your use
case (it usually is, since closing questions and final decisions are often the
most worth remembering), don't rely on eviction for it. Call the bridge
directly at whatever point your application considers a turn or a session
finished:

```python theme={null}
# After the agent responds, alongside (or instead of) leaving it to Memory:
await bridge.remember(f"User: {user_message}\nAssistant: {agent_reply}")
```

This is the same `MemoryBridge` the block itself uses. `bridge.remember(...)`
is already public API, so this needs no new code, just a call at your own
turn or session boundary. It fires immediately rather than waiting on buffer
pressure, so it's a reasonable choice even for turns that aren't the last
one, if you'd rather not depend on `token_limit` timing at all.

### Use a durable database URI

`Memory.from_defaults()` defaults to an in-memory SQLite database when
`async_database_uri` is left unset. That default does not survive across
separate agent runs: reusing the same `memory` object for a second
`agent.run(...)` call (the normal one-run-per-turn pattern) starts the
short-term buffer over from empty, so it never accumulates enough history
across turns for this block to receive anything. Point `async_database_uri`
at a real file or database for any multi-turn session.

## Scoping

```python theme={null}
bridge = MemoryBridge(
    api_key="anona_live_...",
    space_id="assistant",
    user_id="customer-42",
)
```

A memory written under one `user_id` is only ever returned to a query carrying
the same `user_id`, so a single space can serve every end user of your app.

<Note>
  `Memory.from_defaults(session_id=...)` is LlamaIndex's own key for its
  short-term buffer, and it never reaches Anona. `Memory.aget()`/`Memory.aput()`
  call this block with only `messages`, never `session_id`, so Anona-side
  isolation comes exclusively from the `user_id` / `agent_id` / `session_id`
  fixed on the `MemoryBridge` at construction time. Two different LlamaIndex
  sessions sharing one `MemoryBridge` share the same Anona memories, so scope
  each end user's `Memory` to its own `MemoryBridge` (or at least its own
  `user_id`), not just its own `session_id`.
</Note>

## Failure behaviour

If Anona is unreachable the block yields no memories and the agent continues.
Failures are logged, never raised.
