LlamaIndex integration — CLS++ as a pluggable memory block
clsplusplus.integrations.llamaindex.CLSMemoryBlock is a native subclass of
llama_index.core.memory.BaseMemoryBlock
— a pluggable memory_block you hand to Memory.from_defaults(memory_blocks=[...]).
It routes the block's get/put through the CLS++ brain, so your agent's long-term
memory is persistent, shared across processes/machines, and reusable across AI
vendors that point at the same CLS++ namespace.
Implemented against llama-index-core 0.14.x (the modern Memory +
memory_blocks API; floor >=0.12.0). BaseMemoryBlock is a pydantic model
generic over T = str | list[ContentBlock] | list[ChatMessage] and declares two
abstract methods a concrete block must implement:
async def _aget(self, messages: Optional[List[ChatMessage]] = None, **block_kwargs) -> T
async def _aput(self, messages: List[ChatMessage]) -> None
CLSMemoryBlock parametrizes T = str: _aget returns recalled facts as a
text block that LlamaIndex inserts into its memory template, and _aput
persists new messages. It also overrides the optional atruncate(content, n).
Install
pip install clsplusplus[llamaindex]
llama-index-core is an optional extra. The base package imports fine
without it (import clsplusplus and import clsplusplus.integrations never
require it); only this integration needs it installed.
Environment
Get an API key from https://www.clsplusplus.com/profile#api-keys, then:
export CLS_API_KEY="cls_live_..."
# optional — defaults to https://www.clsplusplus.com
export CLS_BASE_URL="https://www.clsplusplus.com"
CLSMemoryBlock(user=..., api_key=..., url=...) also accepts these directly;
unset values fall back to the env vars above.
Usage
Add the block to a Memory and pass that memory to your agent. Anything the
conversation flushes is stored in CLS++; on each turn the block recalls the
facts most relevant to the latest user message and injects them.
from llama_index.core.memory import Memory
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.llms.openai import OpenAI
from clsplusplus.integrations.llamaindex import CLSMemoryBlock
# One CLS++ "brain" namespace for this agent/user.
memory = Memory.from_defaults(
session_id="my-session",
memory_blocks=[
CLSMemoryBlock(user="my-agent", default_limit=5),
],
)
agent = FunctionAgent(tools=[], llm=OpenAI(model="gpt-4o-mini"))
# CLS++ recalls relevant long-term memory and injects it into context;
# new messages are persisted back into CLS++ for future sessions.
response = await agent.run("What are my coding preferences?", memory=memory)
print(response)
You can also use the block directly against Memory outside an agent:
memory = Memory.from_defaults(session_id="s1", memory_blocks=[CLSMemoryBlock(user="alice")])
from llama_index.core.base.llms.types import ChatMessage
await memory.aput_messages([ChatMessage(role="user", content="I prefer Python over JS")])
# Later — recalled facts come back inside the assembled memory context:
context = await memory.aget()
Operation mapping & limitations
| LlamaIndex call | CLS++ behavior |
|---|---|
_aput(messages) |
Brain.learn(text, source="llamaindex") for each non-empty message |
_aget(messages, **kw) |
Brain.ask(query, limit) → newline-bulleted str; query = latest user message (or last stored text, or a broad fallback); limit from block_kwargs["limit"] or default_limit |
atruncate(content, n) |
coarse line-oriented trim of the least-relevant trailing lines (CLS++ returns facts in descending relevance) |
CLS++ is a semantic memory (vector recall). Documented, intentional limitations of mapping a chat-memory block onto semantic recall:
- Not a verbatim transcript.
_agetreturns the facts most relevant to the current turn, not a faithful replay of recent history. Keep LlamaIndex's default short-term buffer (you already get it fromMemory.from_defaults) alongside this block when you also need exact recent-turn fidelity. atruncateis coarse. It drops whole trailing (least-relevant) lines under a ~4-chars-per-token heuristic rather than doing token-exact trimming. CLS++ enforces retention server-side.- Sync client under the hood. The CLS++ SDK (
Brain) is synchronous; the async_aget/_aputrun it in a worker thread (asyncio.to_thread) so the event loop is never blocked.
The same memory-block adapter pattern complements the CLS++ adapters for
LangGraph (CLSMemoryStore) and CrewAI (CLSMemoryStorage).