Markdown-first, LLM-driven memory framework organized into a hierarchical Knowledge Tree
Project description
MdMemory
A Markdown-first, LLM-driven memory framework that organizes agent knowledge into a hierarchical Knowledge Tree.
Features
- Human-Readable: Data stored as standard
.mdfiles on the filesystem - LLM-Organized: Uses LLM to automatically determine folder structure and organization
- Context-Aware: Hybrid indexing strategy keeps the root index compact
- Efficient Navigation: Central
index.mdand.registry.jsonPath Map for direct access - User-Scoped Optimization: Each topic is tagged with a user ID, enabling per-user knowledge tree reorganization
- Auto-Compression: Root index automatically compresses into folder links when subdirectories reach 3+ files
Installation
pip install mdmemory
Or with development dependencies:
pip install -e ".[dev]"
Google ADK Integration
To use MdMemory as a memory service for Google ADK agents:
pip install mdmemory[adk]
Quick Start
from mdmemory import MdMemory
# Define your LLM callback function
# It receives messages and should return LLM response as a string
def llm_callback(messages: list) -> str:
"""
LLM callback function that handles LLM provider communication.
You can use any LLM provider: OpenAI, Claude, Gemini, Ollama, etc.
"""
# Example with LiteLLM (supports all major providers)
from litellm import completion
response = completion(model="gpt-3.5-turbo", messages=messages)
return response.choices[0].message.content
# Or use OpenAI directly
# from openai import OpenAI
# client = OpenAI()
# response = client.chat.completions.create(model="gpt-3.5-turbo", messages=messages)
# return response.choices[0].message.content
# Initialize MdMemory with the callback
memory = MdMemory(
llm_callback,
storage_path="./knowledge_base",
optimize_threshold=20
)
# Store a memory WITH explicit topic
topic = memory.store(
usr_id="user123",
query="Decorators are functions that modify other functions...",
topic="python_decorators" # Optional - provide explicit topic
)
# Store a memory WITHOUT topic - LLM generates one automatically
generated_topic = memory.store(
usr_id="user123",
query="List comprehensions are concise ways to create lists in Python..."
# topic parameter omitted - LLM will infer topic from content
)
print(f"Generated topic: {generated_topic}")
# Retrieve the knowledge tree
index = memory.retrieve("user123")
print(index)
# Get a specific topic
content = memory.get("user123", "python_decorators")
print(content)
# Delete a topic
memory.delete("user123", "python_decorators")
# List all topics
topics = memory.list_topics()
print(topics)
# Optimize structure
memory.optimize("user123")
Directory Structure
storage_root/
├── .registry.json # Global Path Map (Topic ID -> Physical Path)
├── index.md # Root Knowledge Tree
└── /categories/ # Auto-created folders
├── coding/
│ ├── index.md # Sub-index
│ └── python.md # Knowledge file
└── finance/
└── taxes.md
Architecture
Core Components
- MdMemory: Main class providing the public API
- PathRegistry: Manages
.registry.jsonfor topic ID -> file path mapping - FrontMatter: Metadata attached to each knowledge file
- LLMResponse: Structured response from LLM decisions
Key Concepts
Hybrid Indexing
- Root
index.md: High-level overview of all knowledge - Sub-folder
index.md: Generated when folder exceedsoptimize_threshold - Compression: Parent index replaced with link to folder index when compressed
LLM Integration
The library queries the LLM for:
- Path Recommendation: Where to store new knowledge
- Frontmatter Generation: Metadata (summary, tags) for files
- Optimization Suggestions: When and how to reorganize structure
User-Scoped Storage
Every stored topic automatically includes a user_id field in its frontmatter metadata. This enables:
- Per-user optimization:
optimize(usr_id)only reorganizes topics belonging to that user - Multi-user support: Multiple users can share the same storage path without interference
- Frontmatter tracking: User ID is persisted in each
.mdfile's YAML frontmatter
Index Compression
When a subdirectory reaches 3+ markdown files, the root index automatically compresses individual entries into a single folder link:
- **Coding/**: See [Coding index](coding/python/index.md)
This keeps the root index compact and navigable regardless of knowledge tree size.
System Prompt
You are the MdMemory Librarian. Your goal is to maintain a clean, hierarchical
Markdown Knowledge Tree. When storing data, choose a logical path. When optimizing,
group related files into sub-directories to keep the root index under 50 lines.
API Reference
__init__(llm_callback, storage_path, optimize_threshold=20)
Initialize MdMemory with an LLM callback function.
Parameters:
llm_callback: Callback function that receives messages and returns LLM response- Signature:
(messages: List[Dict[str, str]]) -> str - Messages format:
[{"role": "user", "content": "prompt"}] - Should return the LLM response as a string (preferably JSON)
- Signature:
storage_path: Root directory path for storing markdown filesoptimize_threshold(optional): Line count threshold for triggering auto-optimization (default: 20)
Example:
# Define a callback for your LLM provider
def llm_callback(messages):
# Use any LLM provider here
from litellm import completion
response = completion(model="gpt-3.5-turbo", messages=messages)
return response.choices[0].message.content
memory = MdMemory(llm_callback, "./knowledge_base")
# Or use built-in callbacks
from mdmemory import LiteLLMCallback, OpenAICallback, AnthropicCallback
memory = MdMemory(LiteLLMCallback("gpt-3.5-turbo"), "./knowledge_base")
memory = MdMemory(OpenAICallback("gpt-4"), "./knowledge_base")
memory = MdMemory(AnthropicCallback("claude-3-sonnet"), "./knowledge_base")
store(usr_id, query, topic=None) -> Optional[str]
Store a new memory item.
Parameters:
usr_id: User identifierquery: Content to store (Markdown text)topic(optional): Topic identifier. If not provided, LLM will generate one from the query content
Returns: The topic ID that was used or generated, or None if storage failed
Example:
# With explicit topic
topic = memory.store("user1", "Content here", topic="my_topic")
# With LLM-generated topic
topic = memory.store("user1", "Content here") # LLM generates topic from content
retrieve(usr_id) -> str
Get the root index (knowledge tree overview).
get(usr_id, topic) -> Optional[str]
Get full content of a specific topic.
delete(usr_id, topic) -> bool
Remove a topic from memory.
optimize(usr_id) -> None
Reorganize knowledge tree structure for a specific user. Scans all topics belonging to usr_id, calls the LLM to suggest grouping related topics into subdirectories, moves files accordingly, and compresses the root index by replacing individual entries with folder links for directories with 3+ files.
Parameters:
usr_id: User identifier (only topics with matchinguser_idin frontmatter will be optimized)
Example:
# Optimize only user123's topics
memory.optimize("user123")
list_topics() -> Dict[str, str]
List all topics in the registry.
Google ADK Integration
MdMemory provides MdMemoryService, a drop-in implementation of Google ADK's BaseMemoryService. This lets your ADK agents use MdMemory's persistent, human-readable Markdown knowledge tree as their long-term memory backend.
Setup
from mdmemory import MdMemoryService
# Create the service (uses default storage path)
memory_service = MdMemoryService()
# Or with custom configuration
memory_service = MdMemoryService(
storage_path="./agent_memory",
optimize_threshold=20,
llm_callback=my_llm_callback, # optional
)
Usage with ADK Runner
from google.adk.agents import Agent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from mdmemory import MdMemoryService
memory_service = MdMemoryService(storage_path="./memory")
session_service = InMemorySessionService()
runner = Runner(
agent=my_agent,
app_name="my_app",
session_service=session_service,
memory_service=memory_service,
)
How It Works
| ADK Method | MdMemory Behavior |
|---|---|
add_session_to_memory(session) |
Converts session events to Markdown, LLM generates semantic topic, stores as .md file |
add_events_to_memory(...) |
Appends new user/agent exchanges to existing session's Markdown file |
add_memory(...) |
Stores explicit MemoryEntry items as Markdown topics |
search_memory(query) |
Fast keyword search across frontmatter summaries; returns matching MemoryEntry objects |
Search Performance
search_memory is optimized for speed:
- Iterates the in-memory registry (O(1) dict lookup)
- Reads only frontmatter (small YAML block) for filtering
- Filters by
user_idbefore reading full content - Keyword matches against topic name and summary
- Only reads full Markdown content for actual matches
Giving Your Agent Memory Tools
from google.adk.tools.preload_memory_tool import PreloadMemoryTool
agent = Agent(
model="gemini-2.0-flash",
name="memory_agent",
instruction="You have access to long-term memory. Use it to recall past conversations.",
tools=[PreloadMemoryTool()],
)
Development
Running Tests
pytest tests/
Code Quality
black src/
ruff check src/
mypy src/
License
MIT
Specification
See spec.md for the full implementation specification.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mdmemory-0.3.0.tar.gz.
File metadata
- Download URL: mdmemory-0.3.0.tar.gz
- Upload date:
- Size: 302.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c69d41752c8396bbf31c6b1a7eb1b7441e31e80261e41b96b4690d0a36875059
|
|
| MD5 |
92a5111bfd9838847b76d299f29f021d
|
|
| BLAKE2b-256 |
f9fad22d4fe5e050ad262af44c769edfdb40366151bfa7100b98994cdae6c8ce
|
Provenance
The following attestation bundles were made for mdmemory-0.3.0.tar.gz:
Publisher:
python-publish.yml on pvkarthikk/MdMemory
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mdmemory-0.3.0.tar.gz -
Subject digest:
c69d41752c8396bbf31c6b1a7eb1b7441e31e80261e41b96b4690d0a36875059 - Sigstore transparency entry: 1239306009
- Sigstore integration time:
-
Permalink:
pvkarthikk/MdMemory@061d3811b133768aeab4cb7508a99d0abe7e4268 -
Branch / Tag:
refs/tags/0.3.0 - Owner: https://github.com/pvkarthikk
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@061d3811b133768aeab4cb7508a99d0abe7e4268 -
Trigger Event:
release
-
Statement type:
File details
Details for the file mdmemory-0.3.0-py3-none-any.whl.
File metadata
- Download URL: mdmemory-0.3.0-py3-none-any.whl
- Upload date:
- Size: 21.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
82c6a7828c70ff7675b7429778eb0d2467ba0722051ebb1722fc5906f121db71
|
|
| MD5 |
76afbac06e686a0a4d78d28a5621b924
|
|
| BLAKE2b-256 |
55fb8bea988441d9d54dd4bc83ff080c11bd0c804da49b412cbee11a60755280
|
Provenance
The following attestation bundles were made for mdmemory-0.3.0-py3-none-any.whl:
Publisher:
python-publish.yml on pvkarthikk/MdMemory
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mdmemory-0.3.0-py3-none-any.whl -
Subject digest:
82c6a7828c70ff7675b7429778eb0d2467ba0722051ebb1722fc5906f121db71 - Sigstore transparency entry: 1239306010
- Sigstore integration time:
-
Permalink:
pvkarthikk/MdMemory@061d3811b133768aeab4cb7508a99d0abe7e4268 -
Branch / Tag:
refs/tags/0.3.0 - Owner: https://github.com/pvkarthikk
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@061d3811b133768aeab4cb7508a99d0abe7e4268 -
Trigger Event:
release
-
Statement type: