Metadata-Version: 2.2
Name: gllm-memory-binary
Version: 0.3.0
Summary: A flexible memory system for Gen AI applications
Author-email: Budi Kurniawan <budi.kurniawan1@gdplabs.id>
Requires-Python: <3.14,>=3.11
Description-Content-Type: text/markdown
Requires-Dist: filetype<2.0.0,>=1.2.0
Requires-Dist: gllm-core-binary>=0.4.0
Requires-Dist: gllm-datastore-binary<0.6.0,>=0.5.0
Requires-Dist: gllm-inference-binary<0.7.0,>=0.6.0
Requires-Dist: gllm-retrieval-binary<0.6.0,>=0.5.28
Requires-Dist: httpx<1.0.0,>=0.28.0
Requires-Dist: langchain-community<2.0.0,>=0.4.0
Requires-Dist: langchain-core<2.0.0,>=1.0.0
Requires-Dist: pydantic<3.0.0,>=2.11.4
Requires-Dist: python-dotenv<2.0.0,>=1.0.0
Requires-Dist: python-json-logger<5.0.0,>=3.3.0
Requires-Dist: python-magic<0.5.0,>=0.4.27; sys_platform != "win32"
Requires-Dist: python-magic-bin<0.5.0,>=0.4.14; sys_platform == "win32"
Requires-Dist: pyyaml<7.0.0,>=6.0
Requires-Dist: scipy<2.0.0,>=1.15.1
Requires-Dist: typing-extensions<5.0.0,>=4.8.0
Requires-Dist: cryptography>=46.0.5
Provides-Extra: dev
Requires-Dist: coverage<8.0.0,>=7.4.4; extra == "dev"
Requires-Dist: mypy<2.0.0,>=1.15.0; extra == "dev"
Requires-Dist: pre-commit<5.0.0,>=3.7.0; extra == "dev"
Requires-Dist: pytest<10.0.0,>=9.0.0; extra == "dev"
Requires-Dist: pytest-asyncio<2.0.0,>=1.3.0; extra == "dev"
Requires-Dist: pytest-cov<8.0.0,>=6.0.0; extra == "dev"
Requires-Dist: ruff<1.0.0,>=0.6.7; extra == "dev"
Provides-Extra: mem0ai
Requires-Dist: mem0ai<3.0.0,>=0.1.117; extra == "mem0ai"
Requires-Dist: spacy<4.0.0,>=3.7.0; extra == "mem0ai"
Provides-Extra: openai
Requires-Dist: gllm-inference-binary[openai]<0.7.0,>=0.6.0; extra == "openai"
Provides-Extra: kg
Requires-Dist: gllm-datastore-binary[kg]<0.6.0,>=0.5.0; extra == "kg"
Requires-Dist: gllm-misc-binary<0.10.0,>=0.9.0; extra == "kg"
Provides-Extra: vector-stores
Requires-Dist: elasticsearch<10.0.0,>=9.0.0; extra == "vector-stores"

# GLLM Memory

## Description

Memory layer for AI agents. The public API is `MemoryManager`. You can use it in two ways:

1. **HTTP mode**: use `api_key` and optional `host`
2. **SDK mode**: use `MemoryManagerConfig` and pass `config=...`

In SDK mode, you can register your own LLM, embedding model, memory store, and optional reranker without exposing backend-specific config to application code.

## TL;DR / 30-Second Example

Fastest HTTP mode example:

```python
from gllm_inference.schema.message import Message
from gllm_memory import MemoryManager
from gllm_memory.enums import MemoryScope

memory_manager = MemoryManager(api_key="your_mem0_api_key")

await memory_manager.add(
    user_id="user_123",
    agent_id="agent_456",
    messages=[Message.user("I love pizza")],
    scopes={MemoryScope.USER},
)

results = await memory_manager.search(
    query="What does the user like?",
    user_id="user_123",
    scopes={MemoryScope.USER},
)
```

For the recommended SDK mode setup with `MemoryManagerConfig`, see [SDK Mode](#sdk-mode).

## Installation & Setup

### Requirements

1. **Python 3.11+** — [Install here](https://www.python.org/downloads/)
2. **pip** or **uv** — [pip](https://pip.pypa.io/en/stable/installation/), [uv](https://docs.astral.sh/uv/getting-started/installation/)
3. **gcloud CLI** — [Install here](https://cloud.google.com/sdk/docs/install)
4. **Git** — only needed for local development from a cloned repository

### Authentication

Use this once when you need internal packages or local development setup:

```bash
gcloud auth login
export UV_INDEX_GEN_AI_INTERNAL_USERNAME=oauth2accesstoken
export UV_INDEX_GEN_AI_INTERNAL_PASSWORD="$(gcloud auth print-access-token)"
export UV_INDEX_GEN_AI_USERNAME=oauth2accesstoken
export UV_INDEX_GEN_AI_PASSWORD="$(gcloud auth print-access-token)"
```

### Install from Artifact Registry

```bash
uv pip install \
  --extra-index-url "https://oauth2accesstoken:$(gcloud auth print-access-token)@glsdk.gdplabs.id/gen-ai-internal/simple/" \
  gllm-memory
```

### Install from Local Clone

```bash
git clone git@github.com:GDP-ADMIN/gl-sdk.git
cd gl-sdk/libs/gllm-memory
pip install -e .
```

For the full local development setup with project tooling:

```bash
make setup
source .venv/bin/activate
```

### Runtime Notes

1. HTTP mode uses `MEM0_API_KEY` and optional `MEM0_HOST`.
2. SDK mode uses `MemoryManagerConfig(...)` and lets your app register LM, embedding, memory store, and optional retrieval reranker.

### Optional Dependencies

1. OpenAI-based SDK examples require OpenAI support from `gllm-inference`, for example `gllm-inference[openai]`.
2. Knowledge graph examples require the KG dependencies used by this repository setup.

Typical environment variables:

| Variable          | Role                                                                                      |
|-------------------|-------------------------------------------------------------------------------------------|
| `MEM0_API_KEY`    | Required for the HTTP client when not passed in code.                                     |
| `MEM0_HOST`       | Optional; base URL for self-hosted Mem0 API.                                              |
| `MEMORY_PROVIDER` | Optional; default is Mem0 (`mem0`).                                                       |
| `TIMEOUT_SEC`     | Optional; request timeout in seconds (default `30`). Used when building clients from env. |

Do not commit secrets to git.

## Architecture

The system follows a layered architecture below:

```text
┌──────────────────────────────────────────────────────────────┐
│                    Application Layer                         │
├──────────────────────────────────────────────────────────────┤
│                    Memory Manager                            │
├──────────────────────────────────────────────────────────────┤
│                    Memory Client (Base)                      │
├──────────────────────────────────────────────────────────────┤
│                    Provider Layer (Mem0)                     │
├──────────────────────────────────────────────────────────────┤
│                    Mem0 Platform (HTTP client or Python SDK) │
└──────────────────────────────────────────────────────────────┘
```

<a id="sdk-mode"></a>

## 🧩 SDK Mode With `MemoryManagerConfig`

Use this mode if you want to:

1. register memory LLM and embedding invoker runtimes
2. configure an optional reranker
3. keep application code independent from backend-specific config shape

`gllm-memory` does not create provider-specific LM or embedding invokers for you in normal SDK usage. Your application builds the LM invoker, optional fallback invokers, one embedding invoker, and then wraps the memory LLM path with the library-owned `MemoryLMComponent`.

### SDK Mode Example

Recommended LLM registration:

```python
from gllm_inference.lm_invoker.lm_invoker import BaseLMInvoker
from gllm_inference.lm_invoker.openai_lm_invoker import OpenAILMInvoker
from gllm_memory import MemoryLMComponent, MemoryManagerConfig


def build_openai_lm_invoker(model_name: str) -> OpenAILMInvoker:
    return OpenAILMInvoker(
        model_name=model_name,
        api_key="your_openai_api_key",
    )


def build_fallback_lm_invokers() -> list[BaseLMInvoker]:
    return [
        build_openai_lm_invoker("gpt-4o-mini"),
    ]


def build_lm_component() -> MemoryLMComponent:
    return MemoryLMComponent(
        lm_invoker=build_openai_lm_invoker("gpt-5-nano"),
        fallback_lms=build_fallback_lm_invokers() or None,
    )


def build_em_invoker():
    from gllm_inference.em_invoker.openai_em_invoker import OpenAIEMInvoker

    return OpenAIEMInvoker(
        model_name="text-embedding-3-small",
        api_key="your_openai_api_key",
    )


lm_component = build_lm_component()
em_invoker = build_em_invoker()

config = (
    MemoryManagerConfig.builder()
    .memory_store.elasticsearch(
        host="localhost",
        port=9200,
        collection_name="memories",
        embedding_model_dims=1536,
    )
    .embedding.register(
        em_invoker,
        embedding_dims=1536,
    )
    .llm.register_component(lm_component)
    .reranker.similarity_based(
        em_invoker,
        top_k=5,
    )
    .build()
)
```

In this path, `em_invoker` and the underlying LM invokers are created by your application, while `MemoryLMComponent` is owned by `gllm-memory`. `MemoryManager.instruction` remains the source of truth for memory extraction instructions, and `lm_component` can route from one primary LM to `fallback_lms` when the primary LM fails.

The reranker is optional. If you do not need retrieval reranking, omit `.reranker.similarity_based(...)` from the builder. When configured with `similarity_based(...)`, reranking runs in the external retrieval layer after provider retrieval returns chunks. The provider keeps native backend rerank disabled for this path so the request does not run double reranking. If your installed `gllm_inference` version still has a circular import on `OpenAIEMInvoker`, instantiate the EM invoker with a local lazy import like the example above.

### SDK Mode With Default Config

If you want to use the default SDK setup, you can build an empty config:

```python
from gllm_memory import MemoryManager, MemoryManagerConfig

config = MemoryManagerConfig.builder().build()
memory_manager = MemoryManager(config=config)
```

Default SDK behavior:

1. memory store uses Elasticsearch
2. embedding uses `gllm-inference: EM Invoker` with OpenAI defaults
3. llm uses `gllm-inference` OpenAI defaults
4. reranker is omitted unless configured explicitly

Required environment variables for the default SDK config:

1. `ELASTICSEARCH_HOST`
2. `ELASTICSEARCH_PORT`
3. `ELASTICSEARCH_COLLECTION_NAME`
4. `ELASTICSEARCH_EMBEDDING_MODEL_DIMS`
5. `OPENAI_API_KEY`

Optional environment variables:

1. `ELASTICSEARCH_USER`
2. `ELASTICSEARCH_PASSWORD`
3. `OPENAI_BASE_URL`
4. `OPENAI_MODEL_NAME` (default SDK LLM model override)
5. `OPENAI_EMBEDDING_MODEL` (used by `examples/example_mem0_sdk_client.py`)

## 🌐 HTTP Mode

Use this mode if you want to connect to the HTTP API directly. Point the client at your own server:

```python
from gllm_memory import MemoryManager

manager = MemoryManager(
    api_key="your-api-key",
    host="https://your-mem0-server.com",
)
```

If you want local SDK mode, use `MemoryManager(config=...)` instead of `api_key` and `host`.

### HTTP Mode Example

```python
from gllm_inference.schema.message import Message
from gllm_memory import MemoryManager
from gllm_memory.enums import MemoryScope

memory_manager = MemoryManager(api_key="...", host="...")  # host optional

messages = [
    Message.user("I love pizza"),
    Message.assistant("Noted."),
]
await memory_manager.add(
    user_id="user_123",
    agent_id="agent_456",
    messages=messages,
    scopes={MemoryScope.USER},
    metadata={"conversation_id": "chat_001"},  # Optional
    infer=True,  # Optional, defaults to True
    is_important=False,  # Optional, defaults to False
)

memories = await memory_manager.search(
    query="What does the user like?",
    user_id="user_123",
    scopes={MemoryScope.USER},
    metadata=None,  # Optional
    threshold=0.3,  # Optional, defaults to 0.3
    top_k=10,  # Optional, defaults to 10
    include_important=False,  # Optional, defaults to False
    rerank=False,  # Optional, defaults to False; if True, applies re-ranking to results
)
```

## 🕸️ Knowledge Graph in GLLM Memory

`gllm-memory` can optionally use a Knowledge Graph (KG) so one search flow can combine:

1. normal memory retrieval
2. graph-based facts such as people, companies, places, and relationships

Enable it with the same public API:

```python
memory_manager = MemoryManager(config=config)
```

Recommended setup:

```python
from gllm_inference.lm_invoker.openai_lm_invoker import OpenAILMInvoker
from gllm_memory import MemoryManager, MemoryManagerConfig, Neo4jGraphStoreConfig

memory_lm_component = build_lm_component()
em_invoker = build_em_invoker()

kg_lm_invoker = OpenAILMInvoker(
    model_name="gpt-4o-mini",
    api_key="your_openai_api_key",
)

config = (
    MemoryManagerConfig.builder()
    .memory_store.elasticsearch(
        host="localhost",
        port=9200,
        collection_name="memories",
        embedding_model_dims=1536,
    )
    .embedding.register(
        em_invoker,
        embedding_dims=1536,
    )
    .llm.register_component(memory_lm_component)
    .knowledge_graph.enable(
        lm_invoker=kg_lm_invoker,
        graph_store=Neo4jGraphStoreConfig(
            uri="bolt://localhost:7687",
            user="neo4j",
            password="password",
        ),
    )
    .build()
)

memory_manager = MemoryManager(config=config)
```

`MemoryManager` enables KG automatically when the config contains a `knowledge_graph` section.

Detailed KG flows, storage isolation, update behavior, and delete behavior are documented in [docs/knowledge-graph.md](docs/knowledge-graph.md).

## Core API methods

`MemoryManager` exposes async methods; `query` is required where noted.

Usage examples:

1. SDK mode example: see [SDK Mode With `MemoryManagerConfig`](#sdk-mode)
2. HTTP mode example: see [🌐 HTTP Mode](#-http-mode)

### Methods

- **`add(user_id, agent_id, messages, scopes, metadata, infer, is_important) -> list[Chunk]`** - Add new memories from message objects.
- **`search(query, user_id, agent_id, scopes, metadata, threshold, top_k, include_important, rerank) -> list[Chunk]`** - Search and retrieve memories by query.
- **`list_memories(user_id, agent_id, scopes, metadata, keywords, page, page_size) -> list[Chunk]`** - Get memories with pagination and optional keyword filtering.
- **`update(memory_id, new_content, metadata, user_id, agent_id, scopes, is_important) -> Chunk | None`** - Update one existing memory by ID.
- **`delete(memory_ids, user_id, agent_id, scopes, metadata) -> list[Chunk]`** - Delete memories by IDs or by user or agent identifiers. When KG is enabled, the related KG contribution is also cleaned up.
- **`delete_by_user_query(query, user_id, agent_id, scopes, metadata, threshold, top_k) -> list[Chunk]`** - Delete memories by query. When KG is enabled, the related KG contribution is also cleaned up.

### 🔧 Code Quality

```bash
# Format code with ruff
ruff format gllm_memory/ tests/

# Check code quality
ruff check gllm_memory/ tests/

# Fix auto-fixable issues
ruff check gllm_memory/ tests/ --fix
```

---

## Local Development Utilities

The following Makefile commands are available for quick operations:

### Install uv

```bash
make install-uv
```

### Install Pre-Commit

```bash
make install-pre-commit
```

### Install Dependencies

```bash
make install
```

### Update Dependencies

```bash
make update
```

### Run Tests

```bash
make test
```

---

## Contributing

Please refer to the [Python Style Guide](https://docs.google.com/document/d/1uRggCrHnVfDPBnG641FyQBwUwLoFw0kTzNqRm92vUwM/edit?usp=sharing) for information about code style, documentation standards, and SCA requirements.

### Contributing Steps

1. **Fork and clone** the repository
2. **Set up development environment**:
   ```bash
   # Complete setup: installs uv, configures auth, installs packages, sets up pre-commit
   make setup
   ```

3. **Activate virtual environment**:
   ```bash
   source .venv/bin/activate
   ```

4. **Run tests** to ensure everything works:
   ```bash
   make test
   ```

5. **Make your changes** and ensure tests pass:
   ```bash
   # Make your changes
   # Ensure tests pass
   make test
   ```

6. **Submit a pull request**:
   ```bash
   # Submit a pull request
   git push origin your-branch
   ```
