mentedb-langchain 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,9 @@
1
+ target/
2
+ *.so
3
+ *.dylib
4
+ *.pyd
5
+ *.egg-info/
6
+ __pycache__/
7
+ dist/
8
+ build/
9
+ .eggs/
@@ -0,0 +1,8 @@
1
+ Metadata-Version: 2.4
2
+ Name: mentedb-langchain
3
+ Version: 0.1.0
4
+ Summary: MenteDB integration for LangChain and LangGraph
5
+ License-Expression: Apache-2.0
6
+ Requires-Python: >=3.9
7
+ Requires-Dist: langchain-core>=0.3
8
+ Requires-Dist: mentedb>=0.7.0
@@ -0,0 +1,114 @@
1
+ # mentedb-langchain
2
+
3
+ MenteDB integration for LangChain and LangGraph. Gives your agents persistent, cognitive memory that goes beyond simple vector retrieval.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install mentedb-langchain
9
+ ```
10
+
11
+ ## Components
12
+
13
+ ### MenteDBMemory
14
+
15
+ A LangChain compatible memory backend that stores conversation context in MenteDB. Unlike buffer or summary memory, MenteDBMemory uses hybrid search (vector similarity, tag filtering, temporal decay) to assemble the most relevant context for each turn.
16
+
17
+ ```python
18
+ from mentedb_langchain import MenteDBMemory
19
+ from langchain.chains import ConversationChain
20
+ from langchain_openai import ChatOpenAI
21
+
22
+ memory = MenteDBMemory(
23
+ data_dir="./agent-memory",
24
+ agent_id="my-agent",
25
+ token_budget=4096,
26
+ )
27
+
28
+ chain = ConversationChain(
29
+ llm=ChatOpenAI(),
30
+ memory=memory,
31
+ )
32
+
33
+ chain.predict(input="What database should I use for time series data?")
34
+ chain.predict(input="Tell me more about that recommendation")
35
+ ```
36
+
37
+ ### MenteDBRetriever
38
+
39
+ A LangChain compatible retriever that uses MenteDB hybrid search. Supports optional tag filtering and agent scoping to narrow results.
40
+
41
+ ```python
42
+ from mentedb_langchain import MenteDBRetriever
43
+ from langchain.chains import RetrievalQA
44
+ from langchain_openai import ChatOpenAI
45
+
46
+ retriever = MenteDBRetriever(
47
+ data_dir="./agent-memory",
48
+ k=10,
49
+ tags=["backend", "architecture"],
50
+ )
51
+
52
+ chain = RetrievalQA.from_chain_type(
53
+ llm=ChatOpenAI(),
54
+ retriever=retriever,
55
+ )
56
+
57
+ chain.invoke("What were the key decisions about our database migration?")
58
+ ```
59
+
60
+ ### MenteDBChatHistory
61
+
62
+ Persistent chat history with cognitive tracking. MenteDB stores messages alongside reasoning trajectories, knowledge gaps, and contradiction signals so the agent's memory improves over time.
63
+
64
+ ```python
65
+ from mentedb_langchain import MenteDBChatHistory
66
+
67
+ history = MenteDBChatHistory(
68
+ session_id="session-123",
69
+ data_dir="./agent-memory",
70
+ )
71
+
72
+ history.add_user_message("What database should I use?")
73
+ history.add_ai_message("I recommend PostgreSQL for your use case.")
74
+
75
+ messages = history.messages
76
+ ```
77
+
78
+ ## Usage with LangGraph
79
+
80
+ MenteDB works naturally with LangGraph. Use `MenteDBMemory` as a checkpointer or context source within graph nodes:
81
+
82
+ ```python
83
+ from mentedb_langchain import MenteDBMemory
84
+
85
+ memory = MenteDBMemory(data_dir="./graph-memory", agent_id="planner")
86
+
87
+ def plan_node(state):
88
+ context = memory.load_memory_variables({"input": state["task"]})
89
+ # Use context to inform planning
90
+ return {**state, "context": context}
91
+
92
+ def reflect_node(state):
93
+ memory.save_context(
94
+ inputs={"input": state["task"]},
95
+ outputs={"output": state["result"]},
96
+ )
97
+ return state
98
+ ```
99
+
100
+ ## Configuration
101
+
102
+ All components accept `data_dir` to specify where MenteDB stores its data. For multi agent setups, use `agent_id` to isolate each agent's memory space.
103
+
104
+ | Parameter | Default | Description |
105
+ |---|---|---|
106
+ | `data_dir` | `./mentedb-data` | Path to the MenteDB data directory |
107
+ | `agent_id` | `None` | Optional agent identifier for memory isolation |
108
+ | `token_budget` | `4096` | Maximum tokens for assembled context (MenteDBMemory) |
109
+ | `k` | `10` | Number of results to return (MenteDBRetriever) |
110
+ | `tags` | `None` | Tag filter for retrieval (MenteDBRetriever) |
111
+
112
+ ## License
113
+
114
+ Apache 2.0
@@ -0,0 +1,5 @@
1
+ from mentedb_langchain.memory import MenteDBMemory
2
+ from mentedb_langchain.retriever import MenteDBRetriever
3
+ from mentedb_langchain.chat_history import MenteDBChatHistory
4
+
5
+ __all__ = ["MenteDBMemory", "MenteDBRetriever", "MenteDBChatHistory"]
@@ -0,0 +1,58 @@
1
+ """LangChain chat history backed by MenteDB."""
2
+ from typing import List, Optional
3
+
4
+ from mentedb import MenteDB
5
+
6
+
7
+ class MenteDBChatHistory:
8
+ """Stores chat message history in MenteDB with persistent recall.
9
+
10
+ Each message is stored as an episodic memory, enabling semantic
11
+ search across conversation history and cross-session continuity.
12
+
13
+ Usage:
14
+ from mentedb_langchain import MenteDBChatHistory
15
+
16
+ history = MenteDBChatHistory(
17
+ session_id="session-123",
18
+ data_dir="./agent-memory",
19
+ )
20
+
21
+ history.add_user_message("What database should I use?")
22
+ history.add_ai_message("I recommend PostgreSQL.")
23
+
24
+ messages = history.messages
25
+ """
26
+
27
+ def __init__(self, session_id: str, data_dir: str = "./mentedb-data",
28
+ agent_id: Optional[str] = None):
29
+ self.session_id = session_id
30
+ self.data_dir = data_dir
31
+ self.agent_id = agent_id
32
+ self._db = MenteDB(data_dir)
33
+ self._messages: list = []
34
+
35
+ @property
36
+ def messages(self) -> list:
37
+ return self._messages
38
+
39
+ def add_user_message(self, message: str) -> None:
40
+ self._messages.append({"role": "user", "content": message})
41
+ self._db.store(
42
+ f"[{self.session_id}] User: {message}",
43
+ memory_type="episodic",
44
+ agent_id=self.agent_id,
45
+ tags=["chat_history", self.session_id],
46
+ )
47
+
48
+ def add_ai_message(self, message: str) -> None:
49
+ self._messages.append({"role": "assistant", "content": message})
50
+ self._db.store(
51
+ f"[{self.session_id}] Assistant: {message}",
52
+ memory_type="episodic",
53
+ agent_id=self.agent_id,
54
+ tags=["chat_history", self.session_id],
55
+ )
56
+
57
+ def clear(self) -> None:
58
+ self._messages.clear()
@@ -0,0 +1,63 @@
1
+ """LangChain memory backed by MenteDB."""
2
+ from typing import Any, Dict, List, Optional
3
+
4
+ from mentedb import MenteDB
5
+
6
+
7
+ class MenteDBMemory:
8
+ """LangChain compatible memory that stores conversation context in MenteDB.
9
+
10
+ Usage with LangChain:
11
+ from mentedb_langchain import MenteDBMemory
12
+
13
+ memory = MenteDBMemory(data_dir="./agent-memory", agent_id="my-agent")
14
+
15
+ memory.save_context(
16
+ inputs={"input": "What database should I use?"},
17
+ outputs={"output": "I recommend PostgreSQL for your use case."}
18
+ )
19
+
20
+ context = memory.load_memory_variables({"input": "Tell me more about that database"})
21
+ """
22
+
23
+ memory_key: str = "history"
24
+
25
+ def __init__(self, data_dir: str = "./mentedb-data", agent_id: Optional[str] = None,
26
+ token_budget: int = 4096, return_messages: bool = False):
27
+ self.data_dir = data_dir
28
+ self.agent_id = agent_id
29
+ self.token_budget = token_budget
30
+ self.return_messages = return_messages
31
+ self._db = MenteDB(data_dir)
32
+
33
+ @property
34
+ def memory_variables(self) -> List[str]:
35
+ return [self.memory_key]
36
+
37
+ def load_memory_variables(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
38
+ """Load relevant memories based on the current input."""
39
+ query = inputs.get("input", "")
40
+ if not query:
41
+ return {self.memory_key: ""}
42
+ results = self._db.search_text(query, k=10)
43
+ if not results:
44
+ return {self.memory_key: ""}
45
+ texts = []
46
+ for r in results:
47
+ mem = self._db.get_memory(r.id)
48
+ if mem:
49
+ texts.append(mem["content"])
50
+ return {self.memory_key: "\n".join(texts)}
51
+
52
+ def save_context(self, inputs: Dict[str, Any], outputs: Dict[str, str]) -> None:
53
+ """Save a conversation turn as episodic memory."""
54
+ input_text = inputs.get("input", "")
55
+ output_text = outputs.get("output", "")
56
+ content = f"User: {input_text}\nAssistant: {output_text}"
57
+ self._db.store(content, memory_type="episodic", agent_id=self.agent_id)
58
+
59
+ def clear(self) -> None:
60
+ """Clear all memories by closing and reopening."""
61
+ self._db.close()
62
+ self._db = MenteDB(self.data_dir)
63
+
@@ -0,0 +1,53 @@
1
+ """LangChain retriever backed by MenteDB vector search."""
2
+ from typing import List, Optional
3
+
4
+ from mentedb import MenteDB
5
+
6
+
7
+ class MenteDBRetriever:
8
+ """LangChain compatible retriever using MenteDB hybrid search.
9
+
10
+ Combines vector similarity, tag filtering, and temporal relevance
11
+ in a single retrieval call.
12
+
13
+ Usage:
14
+ from mentedb_langchain import MenteDBRetriever
15
+
16
+ retriever = MenteDBRetriever(
17
+ data_dir="./agent-memory",
18
+ k=10,
19
+ tags=["backend", "architecture"],
20
+ )
21
+
22
+ docs = retriever.get_relevant_documents("database migration strategy")
23
+ """
24
+
25
+ def __init__(self, data_dir: str = "./mentedb-data", k: int = 10,
26
+ tags: Optional[List[str]] = None, agent_id: Optional[str] = None):
27
+ self.data_dir = data_dir
28
+ self.k = k
29
+ self.tags = tags
30
+ self.agent_id = agent_id
31
+ self._db = MenteDB(data_dir)
32
+
33
+ def get_relevant_documents(self, query: str) -> list:
34
+ """Retrieve relevant memories as dicts with id, content, and score."""
35
+ results = self._db.search_text(query, k=self.k, tags=self.tags)
36
+ docs = []
37
+ for r in results:
38
+ mem = self._db.get_memory(r.id)
39
+ if mem:
40
+ docs.append({
41
+ "page_content": mem["content"],
42
+ "metadata": {
43
+ "id": r.id,
44
+ "score": r.score,
45
+ "memory_type": mem.get("memory_type", ""),
46
+ "tags": mem.get("tags", []),
47
+ },
48
+ })
49
+ return docs
50
+
51
+ async def aget_relevant_documents(self, query: str) -> list:
52
+ """Async version of get_relevant_documents."""
53
+ return self.get_relevant_documents(query)
@@ -0,0 +1,14 @@
1
+ [project]
2
+ name = "mentedb-langchain"
3
+ version = "0.1.0"
4
+ description = "MenteDB integration for LangChain and LangGraph"
5
+ license = "Apache-2.0"
6
+ requires-python = ">=3.9"
7
+ dependencies = [
8
+ "mentedb>=0.7.0",
9
+ "langchain-core>=0.3",
10
+ ]
11
+
12
+ [build-system]
13
+ requires = ["hatchling"]
14
+ build-backend = "hatchling.build"
@@ -0,0 +1,96 @@
1
+ """Tests for mentedb_langchain integration."""
2
+ from mentedb_langchain.memory import MenteDBMemory
3
+ from mentedb_langchain.retriever import MenteDBRetriever
4
+ from mentedb_langchain.chat_history import MenteDBChatHistory
5
+
6
+
7
+ class TestMenteDBMemory:
8
+ def test_default_construction(self):
9
+ memory = MenteDBMemory()
10
+ assert memory.data_dir == "./mentedb-data"
11
+ assert memory.agent_id is None
12
+ assert memory.token_budget == 4096
13
+ assert memory.return_messages is False
14
+
15
+ def test_custom_construction(self):
16
+ memory = MenteDBMemory(
17
+ data_dir="./custom-dir",
18
+ agent_id="agent-1",
19
+ token_budget=8192,
20
+ return_messages=True,
21
+ )
22
+ assert memory.data_dir == "./custom-dir"
23
+ assert memory.agent_id == "agent-1"
24
+ assert memory.token_budget == 8192
25
+ assert memory.return_messages is True
26
+
27
+ def test_memory_variables(self):
28
+ memory = MenteDBMemory()
29
+ assert memory.memory_variables == ["history"]
30
+
31
+ def test_load_memory_variables(self):
32
+ memory = MenteDBMemory()
33
+ result = memory.load_memory_variables({"input": "test query"})
34
+ assert "history" in result
35
+ assert isinstance(result["history"], str)
36
+
37
+ def test_save_context(self):
38
+ memory = MenteDBMemory()
39
+ memory.save_context(
40
+ inputs={"input": "hello"},
41
+ outputs={"output": "world"},
42
+ )
43
+
44
+ def test_clear(self):
45
+ memory = MenteDBMemory()
46
+ memory.clear()
47
+
48
+
49
+ class TestMenteDBRetriever:
50
+ def test_default_construction(self):
51
+ retriever = MenteDBRetriever()
52
+ assert retriever.data_dir == "./mentedb-data"
53
+ assert retriever.k == 10
54
+ assert retriever.tags is None
55
+ assert retriever.agent_id is None
56
+
57
+ def test_custom_construction(self):
58
+ retriever = MenteDBRetriever(
59
+ data_dir="./custom-dir",
60
+ k=20,
61
+ tags=["backend"],
62
+ agent_id="agent-1",
63
+ )
64
+ assert retriever.k == 20
65
+ assert retriever.tags == ["backend"]
66
+
67
+ def test_get_relevant_documents(self):
68
+ retriever = MenteDBRetriever()
69
+ docs = retriever.get_relevant_documents("test query")
70
+ assert isinstance(docs, list)
71
+
72
+
73
+ class TestMenteDBChatHistory:
74
+ def test_default_construction(self):
75
+ history = MenteDBChatHistory(session_id="s1")
76
+ assert history.session_id == "s1"
77
+ assert history.data_dir == "./mentedb-data"
78
+ assert history.agent_id is None
79
+
80
+ def test_messages_empty(self):
81
+ history = MenteDBChatHistory(session_id="s1")
82
+ assert history.messages == []
83
+
84
+ def test_add_messages(self):
85
+ history = MenteDBChatHistory(session_id="s1")
86
+ history.add_user_message("hello")
87
+ history.add_ai_message("hi there")
88
+ assert len(history.messages) == 2
89
+ assert history.messages[0] == {"role": "user", "content": "hello"}
90
+ assert history.messages[1] == {"role": "assistant", "content": "hi there"}
91
+
92
+ def test_clear(self):
93
+ history = MenteDBChatHistory(session_id="s1")
94
+ history.add_user_message("hello")
95
+ history.clear()
96
+ assert history.messages == []