ppl-memory
ppl (withppl.com) as a memory backend for agent frameworks.
ppl is a personal CRM. This package makes it the memory layer for your agents: agents remember facts about people and recall them in later sessions, without keeping anything in their own context. The memory lives in the user's ppl account, so it survives across sessions, agents, and frameworks.
Supported frameworks (one package, framework extras):
| Framework | Extra | Class |
|---|---|---|
| LangGraph | ppl-memory[langchain] |
PplStore (BaseStore) |
| CrewAI | ppl-memory[crewai] |
PplCrewAIStorage (StorageBackend) |
| AutoGen 0.4 | ppl-memory[autogen] |
PplMemory (Memory) |
| Mem0 | ppl-memory[mem0] |
PplMem0 (MemoryBase) |
Setup
- Create an API token at withppl.com/settings/agents.
- Export it:
export PPL_API_TOKEN="your-token"
Optionally set a default contact for memories: export PPL_DEFAULT_CONTACT="Jane Smith" (name or contact id).
- Install the extra for your framework:
pip install ppl-memory[langchain] # or [crewai], [autogen], or [mem0]
LangGraph
PplStore implements LangGraph's BaseStore. Memories are namespaced per
contact: namespace ("memories", <contact id or name>). put stores a fact
as a note on that contact's timeline; get retrieves it back; search
runs ppl's ranked retrieval over everything ppl knows.
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph
from ppl_memory.langchain import PplStore
store = PplStore(default_contact="Jane Smith")
# remember
store.put(("memories", "Jane Smith"), "coffee", {"fact": "Jane drinks oat milk lattes."})
# recall in a later session
item = store.get(("memories", "Jane Smith"), "coffee")
print(item.value["fact"])
# natural-language search across the whole CRM
for hit in store.search(("memories",), query="What does Jane like to drink?"):
print(hit.value["fact"], hit.score)
# use it in a graph
graph = StateGraph(...).compile(checkpointer=MemorySaver(), store=store)
CrewAI
PplCrewAIStorage implements CrewAI's unified-memory StorageBackend
protocol. Use it as the storage for the unified Memory:
from crewai import Agent, Crew, Task, Process
from crewai.memory import Memory
from ppl_memory.crewai import PplCrewAIStorage
memory = Memory(storage=PplCrewAIStorage(default_contact="Jane Smith"))
crew = Crew(
agents=[...],
tasks=[...],
process=Process.sequential,
memory=memory,
)
memory.remember("The customer prefers email over phone", scope="/clients/acme")
memory.recall("how should we contact acme?", scope="/clients")
save() writes each record as a note on the contact's timeline (contact from
record metadata, a /contacts/<name> scope, or the default contact).
search() scores records with cosine similarity against a local embedding
cache (~/.ppl-memory/crewai_embeddings.json; derived vectors only, the
memory content lives in ppl). delete() removes only notes this backend
created; your other CRM data is untouched.
AutoGen 0.4
PplMemory implements the autogen_core Memory protocol.
import asyncio
from autogen_core.memory import MemoryContent
from autogen_core.model_context import BufferedChatCompletionContext
from ppl_memory.autogen import PplMemory
async def main():
memory = PplMemory(default_contact="Jane Smith")
await memory.add(MemoryContent(
content="Jane's birthday is Friday.",
mime_type="text/plain",
metadata={"contact": "Jane Smith"},
))
result = await memory.query("When is Jane's birthday?")
print(result.results[0].content)
# inject relevant memories into the model context
context = BufferedChatCompletionContext(buffer_size=10)
await memory.update_context(context)
asyncio.run(main())
update_context() takes the latest user message, queries ppl for relevant
memories, and adds them as a system message. clear() is a deliberate no-op:
ppl holds real CRM history and must not be wiped by a memory reset.
Mem0
PplMem0 subclasses mem0's MemoryBase, so it works anywhere a Mem0 memory
store is expected, with Mem0-style add/search result shapes.
from ppl_memory.mem0 import PplMem0
memory = PplMem0(default_contact="Jane Smith")
# add: messages as a string or [{"role": ..., "content": ...}] list
result = memory.add(
[{"role": "user", "content": "Jane's birthday is Friday."}],
user_id="Jane Smith",
)
print(result["results"][0]["id"])
# search
hits = memory.search("When is Jane's birthday?", user_id="Jane Smith")
print(hits["results"][0]["memory"])
# get / get_all / update / delete / history follow the MemoryBase interface
mem = memory.get(result["results"][0]["id"])
memory.update(mem["id"], "Jane's birthday is Saturday, not Friday.")
memory.delete(mem["id"])
user_id selects the ppl contact (id or name); default_contact is the
fallback. delete_all() and reset() are deliberate no-ops: ppl is the
system of record and is never wiped by an adapter.
Raw client
from ppl_memory import PplClient
client = PplClient() # reads PPL_API_TOKEN
client.remember(contact_id=123, fact="Jane prefers email over phone.")
client.ask("How should I reach Jane?") # ranked retrieval
client.search("birthday") # semantic search
client.get_briefing() # morning briefing
Notes
- Memories are stored as notes on contact timelines in ppl. They are visible in the ppl web app, which is the point: the user's memory is inspectable.
- The human never handles the token for framework setup beyond the one-time export; the agent drives the rest via withppl.com/agents.
- Not affiliated with LangChain, CrewAI, or Microsoft. ppl is a standalone personal CRM by Cumulative Systems.
License
MIT
Metadata
Release files for ppl-memory 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ppl_memory-0.2.0.tar.gz | 17.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ppl_memory-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 36.9 kB
Release files / ppl_memory-0.2.0.tar.gz
| Download URL | ppl_memory-0.2.0.tar.gz |
|---|---|
| Size | 17.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6bec419695540a523a674399d6f19cee0b894e17f16c5ac9ecdf7a44e27cb396
|
|
BLAKE2b-256 checksum How to use checksums |
054478ee211a997ecf953c5da4c3f8503920adf4836da6284bd598db545ae3f8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.
Transparency logRelease files / ppl_memory-0.2.0-py3-none-any.whl
| Download URL | ppl_memory-0.2.0-py3-none-any.whl |
|---|---|
| Size | 19.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b5b9e0ace705cfcb28072b1d45c91a31d0728f7d51f3593378ddeba5d253623d
|
|
BLAKE2b-256 checksum How to use checksums |
258ad11df7b0f677b244bb9b2c8ac94364602ab1765ab9d59512ce712d2b9e5e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.
Transparency log