Metadata-Version: 2.4
Name: memory_agent
Version: 2.0.14
Summary: A Python library for advanced memory management in AI agent applications
Author-email: Giuseppe Zileni <giuseppe.zileni@gmail.com>
Project-URL: Homepage, https://gzileni.github.io/memory-agent
Project-URL: Repository, https://github.com/gzileni/memory-agent
Keywords: agent,memory
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE.md
Requires-Dist: aioboto3>=15.1.0
Requires-Dist: aiobotocore>=2.24.0
Requires-Dist: aiofiles>=24.1.0
Requires-Dist: aiohappyeyeballs>=2.6.1
Requires-Dist: aiohttp>=3.11.18
Requires-Dist: aioitertools>=0.12.0
Requires-Dist: aiosignal>=1.3.2
Requires-Dist: annotated-types>=0.7.0
Requires-Dist: anthropic>=0.64.0
Requires-Dist: anyio>=4.9.0
Requires-Dist: attrs>=25.3.0
Requires-Dist: backoff>=2.2.1
Requires-Dist: beautifulsoup4>=4.13.4
Requires-Dist: boto3>=1.39.11
Requires-Dist: botocore>=1.39.11
Requires-Dist: build>=1.2.2.post1
Requires-Dist: certifi>=2025.4.26
Requires-Dist: cffi>=1.17.1
Requires-Dist: chardet>=5.2.0
Requires-Dist: charset-normalizer>=3.4.2
Requires-Dist: click>=8.1.3
Requires-Dist: coloredlogs>=15.0.1
Requires-Dist: contourpy>=1.3.2
Requires-Dist: cryptography>=43.0.0
Requires-Dist: cycler>=0.12.1
Requires-Dist: dataclasses-json>=0.6.7
Requires-Dist: distro>=1.9.0
Requires-Dist: docutils>=0.21.2
Requires-Dist: dydantic>=0.0.8
Requires-Dist: emoji>=2.14.1
Requires-Dist: fastembed>=0.7.0
Requires-Dist: filelock>=3.18.0
Requires-Dist: filetype>=1.2.0
Requires-Dist: flatbuffers>=25.2.10
Requires-Dist: fonttools>=4.58.0
Requires-Dist: fpdf>=1.7.2
Requires-Dist: frozenlist>=1.6.0
Requires-Dist: fsspec>=2024.12.0
Requires-Dist: greenlet>=3.2.2
Requires-Dist: grpcio>=1.74.0
Requires-Dist: h11>=0.16.0
Requires-Dist: h2>=4.2.0
Requires-Dist: hf-xet>=1.1.9
Requires-Dist: hpack>=4.1.0
Requires-Dist: html5lib>=1.1
Requires-Dist: httpcore>=1.0.9
Requires-Dist: httpx>=0.28.1
Requires-Dist: httpx-sse>=0.4.0
Requires-Dist: huggingface-hub>=0.34.4
Requires-Dist: humanfriendly>=10.0
Requires-Dist: hyperframe>=6.1.0
Requires-Dist: id>=1.5.0
Requires-Dist: idna>=3.10
Requires-Dist: iniconfig>=2.1.0
Requires-Dist: jaraco.classes>=3.4.0
Requires-Dist: jaraco.context>=6.0.1
Requires-Dist: jaraco.functools>=4.1.0
Requires-Dist: jiter>=0.9.0
Requires-Dist: jmespath>=1.0.1
Requires-Dist: joblib>=1.5.0
Requires-Dist: json_repair>=0.44.1
Requires-Dist: jsonpatch>=1.33
Requires-Dist: jsonpointer>=2.1
Requires-Dist: keyring>=25.6.0
Requires-Dist: kiwisolver>=1.4.8
Requires-Dist: langchain>=0.3.27
Requires-Dist: langchain-anthropic>=0.3.19
Requires-Dist: langchain-community>=0.3.25
Requires-Dist: langchain-core>=0.3.75
Requires-Dist: langchain-ollama>=0.3.3
Requires-Dist: langchain-openai>=0.3.23
Requires-Dist: langchain-qdrant>=0.2.0
Requires-Dist: langchain-text-splitters>=0.3.10
Requires-Dist: langcodes>=3.5.0
Requires-Dist: langdetect>=1.0.9
Requires-Dist: langgraph>=0.6.6
Requires-Dist: langgraph-checkpoint>=2.1.1
Requires-Dist: langgraph-prebuilt>=0.6.4
Requires-Dist: langgraph-sdk>=0.2.4
Requires-Dist: langmem>=0.0.29
Requires-Dist: langsmith>=0.3.45
Requires-Dist: language_data>=1.3.0
Requires-Dist: loguru>=0.7.3
Requires-Dist: loki-logger-handler>=1.1.2
Requires-Dist: lxml>=5.4.0
Requires-Dist: marisa-trie>=1.3.1
Requires-Dist: Markdown>=3.8.2
Requires-Dist: markdown-it-py>=3.0.0
Requires-Dist: marshmallow>=3.26.1
Requires-Dist: matplotlib>=3.10.3
Requires-Dist: mdurl>=0.1.2
Requires-Dist: mmh3>=5.1.0
Requires-Dist: more-itertools>=10.7.0
Requires-Dist: mpmath>=1.3.0
Requires-Dist: multidict>=6.4.4
Requires-Dist: mypy_extensions>=1.1.0
Requires-Dist: narwhals>=1.43.1
Requires-Dist: neo4j>=5.28.2
Requires-Dist: neo4j-graphrag>=1.9.1
Requires-Dist: nest-asyncio>=1.6.0
Requires-Dist: nh3>=0.2.21
Requires-Dist: nltk>=3.9.1
Requires-Dist: numpy>=2.3.2
Requires-Dist: olefile>=0.47
Requires-Dist: ollama>=0.5.1
Requires-Dist: onnxruntime>=1.22.0
Requires-Dist: openai>=1.86.0
Requires-Dist: orjson>=3.10.18
Requires-Dist: ormsgpack>=1.10.0
Requires-Dist: packaging>=24.2
Requires-Dist: pandas>=2.3.0
Requires-Dist: pillow>=11.2.1
Requires-Dist: plotly>=6.1.2
Requires-Dist: pluggy>=1.6.0
Requires-Dist: portalocker>=2.10.1
Requires-Dist: propcache>=0.3.2
Requires-Dist: protobuf>=4.25.8
Requires-Dist: psutil>=7.0.0
Requires-Dist: py_rust_stemmers>=0.1.5
Requires-Dist: pyaws-s3>=1.0.28
Requires-Dist: pycparser>=2.22
Requires-Dist: pydantic>=2.11.6
Requires-Dist: pydantic-settings>=2.9.1
Requires-Dist: pydantic_core>=2.33.2
Requires-Dist: Pygments>=2.19.1
Requires-Dist: pyparsing>=3.2.3
Requires-Dist: pypdf>=5.6.0
Requires-Dist: pyproject_hooks>=1.2.0
Requires-Dist: pytest>=8.4.1
Requires-Dist: python-dateutil>=2.9.0.post0
Requires-Dist: python-dotenv>=1.1.0
Requires-Dist: python-iso639>=2025.2.18
Requires-Dist: python-magic>=0.4.27
Requires-Dist: python-oxmsg>=0.0.2
Requires-Dist: pytz>=2025.2
Requires-Dist: PyYAML>=6.0.2
Requires-Dist: qdrant-client>=1.14.2
Requires-Dist: RapidFuzz>=3.13.0
Requires-Dist: readme_renderer>=44.0
Requires-Dist: redis>=6.2.0
Requires-Dist: regex>=2024.11.6
Requires-Dist: reportlab>=4.4.2
Requires-Dist: requests>=2.32.4
Requires-Dist: requests-toolbelt>=1.0.0
Requires-Dist: rfc3986>=2.0.0
Requires-Dist: rich>=14.0.0
Requires-Dist: s3transfer>=0.13.1
Requires-Dist: scipy>=1.16.1
Requires-Dist: setuptools>=80.9.0
Requires-Dist: six>=1.17.0
Requires-Dist: sniffio>=1.3.1
Requires-Dist: soupsieve>=2.7
Requires-Dist: SQLAlchemy>=2.0.41
Requires-Dist: sympy>=1.14.0
Requires-Dist: tenacity>=9.1.2
Requires-Dist: tiktoken>=0.9.0
Requires-Dist: tokenizers>=0.21.1
Requires-Dist: tqdm>=4.67.1
Requires-Dist: trustcall>=0.0.39
Requires-Dist: twine>=6.1.0
Requires-Dist: types-PyYAML>=6.0.12.20250516
Requires-Dist: typing-inspect>=0.9.0
Requires-Dist: typing-inspection>=0.4.1
Requires-Dist: typing_extensions>=4.14.0
Requires-Dist: tzdata>=2025.2
Requires-Dist: unstructured>=0.17.2
Requires-Dist: unstructured-client>=0.36.0
Requires-Dist: urllib3>=2.4.0
Requires-Dist: webencodings>=0.5.1
Requires-Dist: wheel>=0.45.1
Requires-Dist: wrapt>=1.17.2
Requires-Dist: xxhash>=3.5.0
Requires-Dist: yarl>=1.20.1
Requires-Dist: zstandard>=0.23.0
Dynamic: license-file

# memory-agent  

[![View on GitHub](https://img.shields.io/badge/View%20on-GitHub-181717?style=for-the-badge&logo=github)](https://github.com/gzileni/memory-agent)  
[![GitHub stars](https://img.shields.io/github/stars/gzileni/memory-agent?style=social)](https://github.com/gzileni/memory-agent/stargazers)  
[![GitHub forks](https://img.shields.io/github/forks/gzileni/memory-agent?style=social)](https://github.com/gzileni/memory-agent/network)  

The library allows managing both [**persistence**](https://langchain-ai.github.io/langgraph/how-tos/persistence/) and [**memory**](https://langchain-ai.github.io/langgraph/concepts/memory/#what-is-memory) for a **LangGraph** agent.

**memory-agent** uses **Redis** as the backend for **short‑term memory** and **long‑term persistence** and **semantic search**.

![memory-agent](./memory-agent.jpeg)

---

## 🔑 Key Features

- **Dual-layer memory system**
  - **Short-term memory with Redis** → fast, volatile storage with TTL for active sessions.
  - **Long-term persistence with Qdrant** → semantic search, embeddings, and cross‑session retrieval.
- **Integration with LangGraph** → stateful LLM agents with checkpoints and memory tools.
- **Multi-LLM**
  - OpenAI (via `AgentOpenAI`)
  - Ollama (via `AgentOllama`) for local inference
- **Flexible embeddings**
  - OpenAI embeddings (default)
  - Ollama embeddings (e.g., `nomic-embed-text`)
- **Automatic memory management**
  - Summarization and reflection to compress context
- **Observability**
  - Structured logging, compatible with **Grafana/Loki**
- **Easy installation & deployment**
  - `pip install`
  - [Docker‑ready](./docker/README.md)

---

## 🧠 Memory vs 🗃️ Persistence

| Function        | Database | Why |
|-----------------|----------|-----|
| **Memory**      | Redis    | Performance, TTL, fast session context |
| **Persistence** | Redis    | Vector search, long‑term storage |

---

## 📦 Installation

```bash
pip install memory-agent
```

For local use with **Ollama** or local embeddings:
- Install Ollama: https://ollama.ai

---

## ▶️ Usage examples (repository root)

The examples show how to configure the agent, send messages (including **streaming**) and share memory between different agents.

### 1) [`demo.py`](./demo.py) — Quick start with Ollama + memory

What it does:
1. Saves to context: `"My name is Giuseppe. Remember that."`  
2. Asks a factoid: `"What is the capital of France?"` (streaming)  
3. Retrieves from **short‑term memory**: `"What is my name?"` (streaming)

Essential snippet (simplified):
```python
from memory_agent.agent.ollama import AgentOllama
from demo_config import thread_id, user_id, session_id, model_ollama, redis_config,     model_embedding_vs_config, model_embedding_config, qdrant_config, collection_config

agent = AgentOllama(
    thread_id=thread_id,
    user_id=user_id,
    session_id=session_id,
    model_config=model_ollama,
    redis_config=redis_config,
    qdrant_config=qdrant_config,
    collection_config=collection_config,
    embedding_store_config=model_embedding_vs_config,
    embedding_model_config=model_embedding_config,
)

# Non-streaming call
text = await agent.invoke("My name is Giuseppe. Remember that.")

# Streaming call
async for token in agent.invoke_stream("What is the capital of France?"):
    print(token, end="")

# Retrieve from context
async for token in agent.invoke_stream("What is my name?"):
    print(token, end="")
```

Run:
```bash
python demo.py
```

What to expect:
- On the first request the agent stores the information (“Giuseppe”).  
- On the third request the agent should answer with the previously provided name.

---

### 2) [`demo_config.py`](./demo_config.py) — Centralized configuration

This file defines **all parameters** used by the examples:

- **Session identifiers**:
  ```python
  thread_id = "thread_demo"
  user_id = "user_demo"
  session_id = "session_demo"
  ```
- **LLM model (Ollama)**:
  ```python
  model_ollama = {
      "model": "llama3.1",
      "model_provider": "ollama",
      "api_key": None,
      "base_url": "http://localhost:11434",
      "temperature": 0.5,
  }
  ```
- **Qdrant**:
  ```python
  qdrant_config = {
      "url": "http://localhost:6333",
  }
  ```
- **Embeddings (via Ollama)**:
  ```python
  model_embedding_config = {
      "name": "nomic-embed-text",
      "url": "http://localhost:11434"
  }
  ```
- **Vector Store / Collection** (example): COSINE distance with `qdrant_client.http.models.Distance.COSINE`.  
- **Redis**: connection/TTL parameters for short‑term memory.

> Modify these values to point to your Redis/Qdrant/Ollama instances. Other examples import directly from `demo_config.py`.

---

### 3) [`demo_mem_shared.py`](./demo_mem_shared.py) — Shared memory between two agents (LangGraph)

This example shows how **two distinct agents** can **share the same memory**.  
The idea is to create two `AgentOllama` instances (e.g., `agent_1` and `agent_2`) that use **the same backends** (Redis + Qdrant) and **the same relevant identifiers** (e.g., collection, user, thread), so that what the first agent stores is available to the second.

Flow:
1. `agent_1` receives: `"My name is Giuseppe. Remember that."` and stores it.  
2. `agent_2` receives: `"What is my name?"` and retrieves the answer from shared memory.

Essential snippet (simplified):
```python
agent_1 = AgentOllama(... shared ...)
agent_2 = AgentOllama(... shared ...)

await agent_1.invoke("My name is Giuseppe. Remember that.")

# The other agent pulls from the same memory
answer = await agent_2.invoke("What is my name?")
print(answer)  # → "Your name is Giuseppe" (expected)
```

Run:
```bash
python demo_mem_shared.py
```

> This pattern is useful when multiple services/workers collaborate on the same user or conversation, leveraging **Redis** for short‑term state and **Qdrant** for persistence/semantic search across sessions.

---

## ⚙️ Prerequisites

- **Redis** running (used for short‑term memory)
- **Ollama** running (LLM and optionally embeddings)
- **OpenAI** API KEY to make request to OpenAI API
- Correct variables/URLs in `demo_config.py`

---

## [Docker](./docker/README.md)

---

## 🧪 Tips

- For **multi‑worker** environments, ensure `thread_id`, `user_id` and `session_id` and collection keys are consistent across processes that need to share memory.  
- To separate memories of different agents, use **distinct session/thread IDs** or different collections in Qdrant.  
- Tune model `temperature` and pruning/summarization parameters to balance cost/quality/context.

---

## 🛠️ Troubleshooting

- **Doesn't retrieve memory** → check Redis reachability and that IDs (thread/user/session) are consistent between requests.  
- **Semantic search not effective** → verify embeddings are enabled (e.g., `nomic-embed-text`) and that Qdrant has the correct collection.  
- **Streaming prints nothing** → ensure you iterate the `invoke_stream(...)` generator and do `print(token, end="")`.

---

## [📄 License MIT](./LICENSE.md)
