Metadata-Version: 2.4
Name: mandol
Version: 0.1.0a1
Summary: Mandol — Agent Memory System with hierarchical, episodic and entity-relation triple-tower retrieval.
Author: AgentMemorySystem Team
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/AgentCombo/Mandol
Project-URL: Repository, https://github.com/AgentCombo/Mandol
Project-URL: Issues, https://github.com/AgentCombo/Mandol/issues
Keywords: memory,agent,rag,retrieval,llm,knowledge-graph
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: <3.13,>=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy<2.0,>=1.26
Requires-Dist: numba<0.62,>=0.60
Requires-Dist: scipy>=1.13
Requires-Dist: pandas>=2.1
Requires-Dist: pyarrow>=15
Requires-Dist: openpyxl>=3.1
Requires-Dist: orjson>=3.10
Requires-Dist: tqdm>=4.66
Requires-Dist: colorlog>=6.8
Requires-Dist: requests>=2.32
Requires-Dist: httpx[socks]>=0.27
Requires-Dist: pydantic<3.0,>=2.9
Requires-Dist: pydantic-settings<3.0,>=2.6
Requires-Dist: fastapi<1.0,>=0.115
Requires-Dist: uvicorn[standard]<1.0,>=0.30
Requires-Dist: networkx>=3.2
Requires-Dist: rustworkx>=0.15
Requires-Dist: igraph>=0.11
Requires-Dist: leidenalg>=0.10
Requires-Dist: duckdb==1.2.2
Requires-Dist: faiss-cpu>=1.8
Requires-Dist: torch==2.8.0
Requires-Dist: torchvision==0.23.0
Requires-Dist: torchaudio==2.8.0
Requires-Dist: transformers<5.0,>=4.40
Requires-Dist: sentence-transformers<6.0,>=5.2
Requires-Dist: safetensors>=0.4
Requires-Dist: pillow>=10.0
Requires-Dist: huggingface_hub[cli]>=0.23.0
Requires-Dist: openai>=1.40
Requires-Dist: tenacity>=9.1
Requires-Dist: tiktoken>=0.7
Requires-Dist: dashscope>=1.20
Requires-Dist: nltk>=3.9
Requires-Dist: regex>=2023.12
Requires-Dist: jieba>=0.42
Requires-Dist: json-repair>=0.30
Requires-Dist: rank-bm25>=0.2.2
Requires-Dist: scikit-learn>=1.4
Requires-Dist: spacy==3.8.14
Requires-Dist: langchain>=0.3
Requires-Dist: langchain-text-splitters>=0.3
Requires-Dist: bert-score>=0.3.13
Requires-Dist: rouge>=1.0.1
Requires-Dist: rouge-score>=0.1.2
Requires-Dist: tabulate>=0.9
Requires-Dist: python-dotenv>=1.0
Requires-Dist: gdown>=5.2
Requires-Dist: zep-cloud>=2.22
Requires-Dist: pymilvus>=2.5
Requires-Dist: neo4j>=5.20
Requires-Dist: python-pptx>=1.0
Requires-Dist: llmlingua>=0.2.2
Provides-Extra: cuda
Requires-Dist: flash-attn==2.8.3; extra == "cuda"
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: datasets>=2.20; extra == "dev"
Requires-Dist: ruff>=0.6; extra == "dev"
Requires-Dist: matplotlib>=3.8; extra == "dev"
Provides-Extra: docs
Requires-Dist: sphinx>=7.0; extra == "docs"
Requires-Dist: furo; extra == "docs"
Requires-Dist: myst-parser; extra == "docs"
Requires-Dist: sphinxcontrib-mermaid; extra == "docs"
Requires-Dist: sphinx-autobuild; extra == "docs"
Dynamic: license-file

# Mandol

> Mandol: an in-memory semantic memory runtime for agent systems.

[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
[![Python](https://img.shields.io/badge/Python-3.12-blue.svg)](https://www.python.org/)
[![Homepage](https://img.shields.io/badge/Homepage-agentcombo.github.io%2FMandol-blue)](https://agentcombo.github.io/Mandol)
[![Docs](https://img.shields.io/badge/Docs-agentcombo.github.io%2FMandol%2Fdocs-green)](https://agentcombo.github.io/Mandol/docs)
[![Paper](https://img.shields.io/badge/arXiv-2606.29778-b31b1b.svg)](https://arxiv.org/abs/2606.29778)

[English](README.md) | [Chinese](README_CN.md)

![Mandol Overview](README.assets/Mandol-overview-v2.png)

## Current Scope

This repository exposes the `mandol` Python package under `src/mandol` and the
paper reproduction workflows under `benchmark_locomo`,
`benchmark_longmemeval`, and `benchmark_self_host`.

The public Python surface is centered on:

- `MemoryUnit`: the basic memory record.
- `MemorySpace`: a tree-like logical namespace for unit membership.
- `SemanticMap`: in-memory unit storage, embedding generation, FAISS indexing,
  sparse retrieval support, persistence, and space-filtered similarity search.
- `SemanticGraph`: a graph layer over memory units and spaces, with relationship
  APIs, graph traversal, retrieval helpers, L2 storage support, and sandboxed
  persistence.
- `MultiRetriever`: BM25, SPLADE, cosine, graph expansion, score fusion, and
  reranker orchestration.
- `TripleTowerRetriever`: hierarchical, entity-relation, and episodic retrieval
  orchestration for already-built memory spaces.
- `memory_router`: LoCoMo and LongMemEval routing policies used by the paper
  router + quantification workflows.

Older notes in the repository may mention `MemorySystem`, `Uid`,
`mandol.ports`, or `mandol.retrieval.pipeline.HybridRetriever`. Those names are
not part of the current package exports. Maintained docs and examples use
`MemoryUnit`, `SemanticMap`, `SemanticGraph`, `MultiRetriever`, and the current
subpackages directly.

## Requirements

- Python `>=3.12,<3.13`
- Linux for the full research/runtime stack
- `uv` for reproducible local environments
- Provider keys for model-backed reproduction runs

The default dependency set in `pyproject.toml` is intentionally broad. It
contains Torch, transformers, sentence-transformers, FAISS CPU, DuckDB, graph
libraries, LLM clients, retrieval/rerank tools, benchmark dependencies, and
optional integration clients needed by the artifact scripts.

## Environment Setup

Install `uv` if it is not already available:

```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```

Create the base runtime environment from the repository root:

```bash
uv sync
```

For day-to-day development and documentation work:

```bash
uv sync --extra dev --extra docs --group spacy-model
```

For paper reproduction and performance runs, install the full artifact stack.
The performance numbers reported for the paper were measured with the relevant
extras installed; use this full path when comparing throughput:

```bash
uv sync --extra dev --extra cuda --group spacy-model
```

If your machine does not have a CUDA/flash-attention-compatible setup, omit
`--extra cuda`. The workflows still run, but retrieval and reranking throughput
may differ from the paper performance setting.
The `cuda` extra is pinned to a Linux x86_64 / Python 3.12 / Torch 2.8 /
CUDA 12 flash-attention wheel for the paper artifact. If this wheel does not
match your platform, omit `--extra cuda` or install a compatible flash-attn
build manually.

After syncing, verify the local editable package:

```bash
uv run python -c "import mandol; print(mandol.__version__)"
```

## Installing Mandol As A Package

For development from this checkout, `uv sync` installs the local `src/mandol`
package into the environment. To build the same artifacts that would be uploaded
to PyPI:

```bash
uv build
```

To test the built wheel locally:

```bash
uv pip install --force-reinstall dist/mandol-*.whl
uv run python -c "from mandol import MemoryUnit, SemanticGraph, SemanticMap; print('ok')"
```

After a public PyPI release, users can install the package with:

```bash
python -m pip install mandol
```

The benchmark directories are repository artifacts, not part of the runtime
package. Use the source checkout when reproducing paper results.

## Optional Acceleration

Mandol runs without acceleration extras, but the paper artifact uses the
following optional paths for higher throughput:

- `--extra cuda`: installs the flash-attention extra declared in
  `pyproject.toml`. The code only passes flash-attention options when the
  dependency is available.
- `--group spacy-model`: installs the large English spaCy model used by some
  extraction and retrieval utilities. Tokenization falls back where supported,
  but the full artifact environment should include it.
- `RERANKER_BACKEND=vllm`: routes compatible reranker scoring through a vLLM
  HTTP endpoint when available.
- Local model caches: pre-download Hugging Face and sentence-transformers
  models on shared machines to avoid counting first-run downloads in benchmark
  timing.

Example vLLM reranker configuration:

```bash
export RERANKER_BACKEND=vllm
export VLLM_API_URL=http://127.0.0.1:8000/score
export VLLM_API_KEY=EMPTY
```

## Provider Keys

Runtime configuration is read through `mandol.utils.config_manager.settings`.
The project root `.env` and system environment variables are supported. Common
keys include:

```bash
export DASHSCOPE_API_KEY=...
export CLOSEAI_API_KEY=...
export OPENAI_API_KEY=...
export OPENROUTER_API_KEY=...
export SILICONFLOW_API_KEY=...
export CSTCLOUD_API_KEY=...
export HF_TOKEN=...
```

`CLOSEAI_API_KEY` falls back to `OPENAI_API_KEY` in the current provider
configuration. `CLOSEAI_*` is an OpenAI-compatible provider alias used by the
paper artifact configuration. If you do not use this gateway, set
`OPENAI_API_KEY` or map the model alias to your own provider. Use
`env.template` as the local environment template; never commit `.env` files.

## Dataset Preparation

Large public datasets and generated graph artifacts are intentionally ignored by
Git. Each dataset directory contains a README with source links and placement
instructions.

LoCoMo10:

```bash
mkdir -p benchmark_locomo/dataset/locomo
curl -fL https://raw.githubusercontent.com/snap-research/locomo/main/data/locomo10.json \
  -o benchmark_locomo/dataset/locomo/locomo10.json

mkdir -p benchmark_self_host/locomo10/dataset
cp benchmark_locomo/dataset/locomo/locomo10.json \
  benchmark_self_host/locomo10/dataset/locomo10.json
```

LongMemEval small split:

```bash
mkdir -p benchmark_longmemeval/dataset/LongMemEval
curl -fL https://huggingface.co/datasets/xiaowu0162/longmemeval-cleaned/resolve/main/longmemeval_s_cleaned.json \
  -o benchmark_longmemeval/dataset/LongMemEval/longmemeval_s_cleaned.json

mkdir -p benchmark_self_host/longmemeval/dataset
cp benchmark_longmemeval/dataset/LongMemEval/longmemeval_s_cleaned.json \
  benchmark_self_host/longmemeval/dataset/longmemeval_s_cleaned.json
```

LongMemEval medium split is only needed for `--dataset-size m`:

```bash
curl -fL https://huggingface.co/datasets/xiaowu0162/longmemeval-cleaned/resolve/main/longmemeval_m_cleaned.json \
  -o benchmark_longmemeval/dataset/LongMemEval/longmemeval_m_cleaned.json
```

Official dataset sources:

- LoCoMo: https://github.com/snap-research/locomo
- LongMemEval: https://github.com/xiaowu0162/LongMemEval
- LongMemEval cleaned files:
  https://huggingface.co/datasets/xiaowu0162/longmemeval-cleaned

## Quick Start

This example uses the light MiniLM preset and disables realtime SPLADE vector
generation so the first run stays small. Creating a `SemanticMap` still loads an
embedding model; if the model is not already cached, sentence-transformers may
download it from Hugging Face.

```python
from mandol import MemoryUnit, SemanticGraph, SemanticMap

semantic_map = SemanticMap(
    embedding_model_name="all-MiniLM-L6-v2",
    use_flash_attention=False,
)
graph = SemanticGraph(semantic_map_instance=semantic_map)

graph.add_unit(
    MemoryUnit(
        uid="msg_001",
        raw_data={"text_content": "Zhang San travelled to Beijing today."},
        metadata={"timestamp": "2026-06-21T09:00:00"},
    ),
    space_names=["demo"],
    generate_sparse_embedding=False,
)
graph.add_unit(
    MemoryUnit(
        uid="msg_002",
        raw_data={"text_content": "He will discuss the Q2 delivery plan."},
        metadata={"timestamp": "2026-06-21T09:05:00"},
    ),
    space_names=["demo"],
    generate_sparse_embedding=False,
)

graph.add_relationship("msg_001", "msg_002", "NEXT")

hits = graph.search_similarity_in_graph(
    query_text="Where did Zhang San go?",
    top_k=3,
    ms_names=["demo"],
    return_score=True,
)

for unit, score in hits:
    print(f"{score:.3f} {unit.uid}: {unit.text_cached}")
```

For multi-method retrieval:

```python
from mandol.retrieval import MultiRetriever

retriever = MultiRetriever(graph)
results = retriever.smart_search(
    "Where did Zhang San go?",
    methods=["bm25", "cosine"],
    top_k=5,
    rerank_method=None,
    space_names=["demo"],
)
```

## Persistence

Use `SemanticGraph.save_graph()` and `SemanticGraph.load_graph()` for complete
state snapshots. They preserve graph topology, semantic map data, retrieval
indices when built, and the sandboxed DuckDB L2 storage copy.

```python
graph.save_graph("./memory_snapshot", build_sparse_vectors=False)

restored = SemanticGraph.load_graph(
    "./memory_snapshot",
    embedding_model_name="all-MiniLM-L6-v2",
    use_flash_attention=False,
)
```

`SemanticMap.save_map()` and `SemanticMap.load_map()` also exist, but they are
map-only APIs and do not preserve the `SemanticGraph` topology.

## Model Configuration

`SemanticMap` has a built-in model registry in
`src/mandol/core/semantic_map.py`. Current presets include:

| Model name | Type | Dim | Notes |
| --- | --- | ---: | --- |
| `Qwen/Qwen3-Embedding-0.6B` | local | 1024 | Default text embedding model |
| `Qwen/Qwen3-Embedding-4B` | local | 2560 | Larger local text model |
| `Qwen/Qwen3-Embedding-8B` | local | 4096 | Larger local text model |
| `Qwen/Qwen3-Embedding-0.6B-remote` | cloud | 1024 | SiliconFlow adapter |
| `BAAI/bge-m3` / `bge-m3` | local | 1024 | Text embedding model |
| `all-MiniLM-L6-v2` | local | 384 | Lightweight CPU-friendly option |
| `jinaai/jina-clip-v2` | local | 1024 | Text and image modalities |
| `jinaai/jina-embeddings-v4` | local | 2048 | Text and image modalities |

## Reproduction Workflows

The paper accuracy numbers use router + quantification workflows over generated
three-tower memory spaces:

- LoCoMo: [benchmark_locomo/REPRODUCE.md](benchmark_locomo/REPRODUCE.md)
- LongMemEval:
  [benchmark_longmemeval/REPRODUCE.md](benchmark_longmemeval/REPRODUCE.md)

The self-host workflows use Mandol's own high-level memory-generation path
without router + quantification:

- LoCoMo10 self-host:
  [benchmark_self_host/locomo10/REPRODUCE.md](benchmark_self_host/locomo10/REPRODUCE.md)
- LongMemEval self-host:
  [benchmark_self_host/longmemeval/REPRODUCE.md](benchmark_self_host/longmemeval/REPRODUCE.md)

Recommended smoke checks before long runs:

```bash
uv run python -m benchmark_locomo.task_eval.locomo_triple_router_quantification --help
uv run python -m benchmark_longmemeval.task_eval.benchmark_triple_router_quantification --help
uv run python -m benchmark_self_host.locomo10.build_graph --help
uv run python -m benchmark_self_host.longmemeval.build_graph --help
```

After the required graph artifacts exist, run a bounded real-LLM task-eval smoke
before launching full benchmark jobs:

```bash
uv run python -m benchmark_locomo.task_eval.locomo_triple_router_quantification \
  --sample-ids conv-30 \
  --max-questions 1 \
  --llm-model gpt-4.1-mini-closeai \
  --llm-evaluate-model gpt-4o-mini-closeai \
  --output-dir benchmark_locomo/task_eval/results/smoke/gpt41_mini

uv run python -m benchmark_longmemeval.task_eval.benchmark_triple_router_quantification \
  --dataset-size s \
  --start-qa 0 \
  --end-qa 0 \
  --max-tests 1 \
  --llm-model gpt-4.1-mini-closeai \
  --llm-evaluate-model gpt-4o-mini-closeai \
  --output-dir benchmark_longmemeval/task_eval/results/smoke/gpt41_mini
```

Paper model roles:

The names below are Mandol provider aliases resolved by the repository
configuration. When using a different provider gateway, keep the roles fixed
but map each alias to an equivalent model endpoint in your local configuration.

- LoCoMo memory/extraction generation: `qwen-3.5-plus-thinking`
- LongMemEval memory/extraction generation: `qwen-3-plus`
- Deduplication: `deepseek-v3.2-dashscope`
- Task-eval evaluated models: `gpt-4.1-mini-closeai` and
  `gpt-4o-mini-closeai`
- Task-eval judge model: `gpt-4o-mini-closeai`

The model names above should be kept fixed when reproducing the paper tables.
The Qwen/DeepSeek models are used for memory generation and deduplication; the
GPT models are used for task evaluation and judging.

## Notes and Limitations

This repository is released as a research artifact and Python reference
implementation for the Mandol paper. It is not intended to be a
production-ready service.

- Full reproduction requires external model providers and local model downloads.
- Reported numbers may vary slightly across hardware, dependency versions,
  model provider versions, and random seeds.
- The `cuda` extra is platform-specific and can be omitted when
  flash-attention is unavailable.
- Large datasets, generated graphs, model caches, and benchmark outputs are
  intentionally excluded from Git.

## Performance Measurement Scope

LoCoMo retrieval-performance tests require unified per-sample graphs. Build them
after the three offline towers have been generated and before running the
fixed-QPS search benchmark:

```bash
bash benchmark_locomo/dataset_maker/build_unified.sh
```

The wrapper calls `benchmark_locomo/dataset_maker/build_unified_graph.py` and
writes unified graph folders to:

```text
benchmark_locomo/dataset/locomo/unified_per_sample_graphs
```

The two reported LoCoMo performance entrypoints measure different API scopes:

- Insertion latency:
  `benchmark_locomo/task_eval/locomo_triple_input_speed.py` schedules requests
  at the target QPS and measures only the body of each
  `SemanticGraph.add_unit(...)` call with `index_update_mode="incremental"` and
  `generate_sparse_embedding=True`. This timed call includes dense embedding
  generation, realtime SPLADE sparse embedding generation, and incremental
  index updates performed by the add path. The reported `latency_ms` excludes
  request scheduling sleep, memory-pool construction, graph initialization,
  warmup, and result-file writing.
- Search latency:
  `benchmark_locomo/task_eval/locomo_triple_smart_search_qps.py` loads a
  unified graph, runs warmup requests, then measures each scheduled
  `MultiRetriever.smart_search(...)` or `smart_search_async(...)` call. The
  reported `latency_ms` covers query dispatch through BM25, cosine, SPLADE,
  score fusion, reranking when `--rerank-method` is set, response parsing, and
  Python async/thread wrapper overhead inside one request. The provided speed
  scripts pass `--rerank-method baai`, so the current smart-search QPS numbers
  include reranking. The metric excludes graph loading, warmup, fixed QPS
  scheduling sleep, and report writing. The report also records
  `retrieval_time_ms` for the base retrieval phase and `rerank_time_ms` for the
  reranking phase.

## Package Layout

```text
src/mandol/
  core/                MemoryUnit, MemorySpace, SemanticMap, SemanticGraph
  retrieval/           MultiRetriever, BM25, SPLADE, cosine, fusion, rerankers
  triple_retrieval/    Three-tower retrieval orchestration
  hierarchical/        Retrieval-facing hierarchical memory components
  entity_relation/     Retrieval-facing entity/relation graph components
  episodic/            Episodic memory retriever
  quantification/      Query expansion, pruning, semantic quantification
  memory_router/       LoCoMo and LongMemEval tower routers
  llm/                 LLM clients and provider wrappers
  storage/             DuckDB and tiered storage helpers
  cluster/             Leiden and DBSCAN clustering helpers
  utils/               Configuration, logging, model management
```

## Documentation

The maintained documentation entry point is `docs/index.rst`. Build it with:

```bash
uv sync --extra docs
uv run sphinx-build -b html docs docs/_build/html
```

The Docusaurus website lives in `website/` and is a separate static front page:

```bash
cd website
npm install
npm run build
```

## Citation

If you use Mandol in your research, please cite the arXiv paper:

```bibtex
@misc{zhang2026mandol,
  title         = {Mandol: An Agglomerative Agent Memory System for Long-Term Conversations},
  author        = {Yuhan Zhang and Zhiyuan Guo and Ziheng Zeng and Wei Wang and Wentao Wu and Lijie Xu},
  year          = {2026},
  eprint        = {2606.29778},
  archivePrefix = {arXiv},
  primaryClass  = {cs.DB},
  doi           = {10.48550/arXiv.2606.29778},
  url           = {https://arxiv.org/abs/2606.29778}
}
```

## License

Mandol is released under the Apache License 2.0. See [LICENSE](LICENSE).
