langchain-synapse 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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2026 Raghuram Parvataneni
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,104 @@
1
+ Metadata-Version: 2.4
2
+ Name: langchain-synapse
3
+ Version: 0.1.0
4
+ Summary: An integration package connecting Synapse memory and LangChain. Privacy-first, zero API calls, pure Python.
5
+ Project-URL: Homepage, https://github.com/raghuram369/synapse
6
+ Project-URL: Repository, https://github.com/raghuram369/synapse
7
+ Project-URL: Issues, https://github.com/raghuram369/synapse/issues
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Requires-Python: >=3.10
20
+ Requires-Dist: langchain-core<1.0.0,>=0.3.0
21
+ Requires-Dist: synapse-ai-memory>=0.3.0
22
+ Description-Content-Type: text/markdown
23
+
24
+ # langchain-synapse
25
+
26
+ An integration package connecting [Synapse](https://github.com/raghuram369/synapse) memory and [LangChain](https://github.com/langchain-ai/langchain).
27
+
28
+ **Privacy-first AI memory** — all data stays local. Zero API calls for storage. Zero external dependencies beyond LangChain and Synapse.
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ pip install langchain-synapse
34
+ ```
35
+
36
+ ## Components
37
+
38
+ ### SynapseMemory
39
+
40
+ Drop-in `BaseMemory` implementation for any LangChain chain:
41
+
42
+ ```python
43
+ from langchain_synapse import SynapseMemory
44
+ from synapse import Synapse
45
+
46
+ syn = Synapse("./agent_memory")
47
+ memory = SynapseMemory(synapse=syn)
48
+
49
+ # Use with any chain
50
+ chain = prompt | llm
51
+ chain.invoke({"input": "hello"}, config={"memory": memory})
52
+ ```
53
+
54
+ ### SynapseChatMessageHistory
55
+
56
+ Persistent chat history with semantic recall:
57
+
58
+ ```python
59
+ from langchain_synapse import SynapseChatMessageHistory
60
+ from synapse import Synapse
61
+
62
+ syn = Synapse("./chat_memory")
63
+ history = SynapseChatMessageHistory(synapse=syn, session_id="user-123")
64
+
65
+ history.add_user_message("I love Italian food")
66
+ history.add_ai_message("Great! Any favorite dish?")
67
+
68
+ # Semantic search — find relevant messages, not just recent ones
69
+ relevant = history.search("What cuisine do they prefer?")
70
+ ```
71
+
72
+ ### SynapseRetriever
73
+
74
+ Use Synapse as a retriever in RAG pipelines:
75
+
76
+ ```python
77
+ from langchain_synapse import SynapseRetriever
78
+ from synapse import Synapse
79
+
80
+ syn = Synapse("./knowledge_base")
81
+ syn.remember("Python was created by Guido van Rossum")
82
+ syn.remember("Rust was created by Graydon Hoare at Mozilla")
83
+
84
+ retriever = SynapseRetriever(synapse=syn, k=5)
85
+
86
+ # Use in a RAG chain
87
+ from langchain_core.runnables import RunnablePassthrough
88
+ chain = (
89
+ {"context": retriever, "question": RunnablePassthrough()}
90
+ | prompt
91
+ | llm
92
+ )
93
+ ```
94
+
95
+ ## Why Synapse?
96
+
97
+ - **🔒 Privacy-first**: All memory stays on your machine. No cloud. No API calls for storage.
98
+ - **🧠 Neuroscience-inspired**: Memory strengthening, decay, and semantic recall — not just vector similarity.
99
+ - **⚡ Zero dependencies**: Synapse itself is pure Python with no external dependencies.
100
+ - **📦 Portable**: `.synapse` files can be shared, versioned, and federated across agents.
101
+
102
+ ## License
103
+
104
+ MIT
@@ -0,0 +1,81 @@
1
+ # langchain-synapse
2
+
3
+ An integration package connecting [Synapse](https://github.com/raghuram369/synapse) memory and [LangChain](https://github.com/langchain-ai/langchain).
4
+
5
+ **Privacy-first AI memory** — all data stays local. Zero API calls for storage. Zero external dependencies beyond LangChain and Synapse.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install langchain-synapse
11
+ ```
12
+
13
+ ## Components
14
+
15
+ ### SynapseMemory
16
+
17
+ Drop-in `BaseMemory` implementation for any LangChain chain:
18
+
19
+ ```python
20
+ from langchain_synapse import SynapseMemory
21
+ from synapse import Synapse
22
+
23
+ syn = Synapse("./agent_memory")
24
+ memory = SynapseMemory(synapse=syn)
25
+
26
+ # Use with any chain
27
+ chain = prompt | llm
28
+ chain.invoke({"input": "hello"}, config={"memory": memory})
29
+ ```
30
+
31
+ ### SynapseChatMessageHistory
32
+
33
+ Persistent chat history with semantic recall:
34
+
35
+ ```python
36
+ from langchain_synapse import SynapseChatMessageHistory
37
+ from synapse import Synapse
38
+
39
+ syn = Synapse("./chat_memory")
40
+ history = SynapseChatMessageHistory(synapse=syn, session_id="user-123")
41
+
42
+ history.add_user_message("I love Italian food")
43
+ history.add_ai_message("Great! Any favorite dish?")
44
+
45
+ # Semantic search — find relevant messages, not just recent ones
46
+ relevant = history.search("What cuisine do they prefer?")
47
+ ```
48
+
49
+ ### SynapseRetriever
50
+
51
+ Use Synapse as a retriever in RAG pipelines:
52
+
53
+ ```python
54
+ from langchain_synapse import SynapseRetriever
55
+ from synapse import Synapse
56
+
57
+ syn = Synapse("./knowledge_base")
58
+ syn.remember("Python was created by Guido van Rossum")
59
+ syn.remember("Rust was created by Graydon Hoare at Mozilla")
60
+
61
+ retriever = SynapseRetriever(synapse=syn, k=5)
62
+
63
+ # Use in a RAG chain
64
+ from langchain_core.runnables import RunnablePassthrough
65
+ chain = (
66
+ {"context": retriever, "question": RunnablePassthrough()}
67
+ | prompt
68
+ | llm
69
+ )
70
+ ```
71
+
72
+ ## Why Synapse?
73
+
74
+ - **🔒 Privacy-first**: All memory stays on your machine. No cloud. No API calls for storage.
75
+ - **🧠 Neuroscience-inspired**: Memory strengthening, decay, and semantic recall — not just vector similarity.
76
+ - **⚡ Zero dependencies**: Synapse itself is pure Python with no external dependencies.
77
+ - **📦 Portable**: `.synapse` files can be shared, versioned, and federated across agents.
78
+
79
+ ## License
80
+
81
+ MIT
@@ -0,0 +1,11 @@
1
+ """LangChain integration for Synapse — privacy-first memory for AI agents."""
2
+
3
+ from langchain_synapse.chat_history import SynapseChatMessageHistory
4
+ from langchain_synapse.memory import SynapseMemory
5
+ from langchain_synapse.retriever import SynapseRetriever
6
+
7
+ __all__ = [
8
+ "SynapseChatMessageHistory",
9
+ "SynapseMemory",
10
+ "SynapseRetriever",
11
+ ]
@@ -0,0 +1,101 @@
1
+ """SynapseChatMessageHistory — LangChain chat history backed by Synapse.
2
+
3
+ Privacy-first: all messages stay local. No cloud storage.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import time
9
+ from typing import Any, Dict, List, Optional, Sequence
10
+
11
+ from langchain_core.chat_history import BaseChatMessageHistory
12
+ from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, SystemMessage
13
+ from synapse import Synapse
14
+
15
+
16
+ class SynapseChatMessageHistory(BaseChatMessageHistory):
17
+ """Persistent chat message history using Synapse as the backend.
18
+
19
+ Unlike simple list-based histories, Synapse provides semantic recall —
20
+ relevant past messages surface automatically based on context.
21
+
22
+ Example::
23
+
24
+ from synapse import Synapse
25
+ from langchain_synapse import SynapseChatMessageHistory
26
+
27
+ syn = Synapse("./chat_memory")
28
+ history = SynapseChatMessageHistory(synapse=syn, session_id="user-123")
29
+
30
+ history.add_user_message("I love Italian food")
31
+ history.add_ai_message("Great! Do you have a favorite dish?")
32
+ """
33
+
34
+ def __init__(
35
+ self,
36
+ synapse: Optional[Synapse] = None,
37
+ path: str = ":memory:",
38
+ session_id: str = "default",
39
+ ):
40
+ self.synapse = synapse or Synapse(path)
41
+ self.session_id = session_id
42
+
43
+ def _make_metadata(self, role: str) -> Dict[str, Any]:
44
+ return {
45
+ "role": role,
46
+ "session_id": self.session_id,
47
+ "source": "langchain_chat_history",
48
+ "timestamp": time.time(),
49
+ }
50
+
51
+ def add_message(self, message: BaseMessage) -> None:
52
+ """Add a LangChain BaseMessage to history."""
53
+ if isinstance(message, HumanMessage):
54
+ role = "human"
55
+ elif isinstance(message, AIMessage):
56
+ role = "ai"
57
+ elif isinstance(message, SystemMessage):
58
+ role = "system"
59
+ else:
60
+ role = "unknown"
61
+
62
+ self.synapse.remember(
63
+ str(message.content),
64
+ memory_type="observation",
65
+ metadata=self._make_metadata(role),
66
+ episode=f"chat-{self.session_id}",
67
+ )
68
+
69
+ @property
70
+ def messages(self) -> List[BaseMessage]:
71
+ """Return all messages in this session, ordered by time."""
72
+ all_memories = self.synapse.recall("", limit=10000)
73
+ session_memories = [
74
+ m for m in all_memories
75
+ if (m.metadata or {}).get("session_id") == self.session_id
76
+ ]
77
+ session_memories.sort(key=lambda m: m.created_at)
78
+
79
+ messages: List[BaseMessage] = []
80
+ for mem in session_memories:
81
+ role = (mem.metadata or {}).get("role", "human")
82
+ if role == "ai":
83
+ messages.append(AIMessage(content=mem.content))
84
+ elif role == "system":
85
+ messages.append(SystemMessage(content=mem.content))
86
+ else:
87
+ messages.append(HumanMessage(content=mem.content))
88
+ return messages
89
+
90
+ def search(self, query: str, limit: int = 5) -> List[Any]:
91
+ """Semantic search across chat history — Synapse's superpower."""
92
+ results = self.synapse.recall(query, limit=limit)
93
+ return [m for m in results
94
+ if (m.metadata or {}).get("session_id") == self.session_id]
95
+
96
+ def clear(self) -> None:
97
+ """Clear all messages in this session."""
98
+ all_memories = self.synapse.recall("", limit=10000)
99
+ for mem in all_memories:
100
+ if (mem.metadata or {}).get("session_id") == self.session_id:
101
+ self.synapse.forget(mem.id)
@@ -0,0 +1,189 @@
1
+ """SynapseCheckpointer — LangGraph checkpoint persistence backed by Synapse.
2
+
3
+ Saves and restores graph state between invocations.
4
+ All data stays local.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import time
11
+ from typing import Any, Dict, Iterator, Optional, Sequence, Tuple
12
+
13
+ from synapse import Synapse
14
+
15
+ try:
16
+ from langgraph.checkpoint.base import (
17
+ BaseCheckpointSaver,
18
+ Checkpoint,
19
+ CheckpointMetadata,
20
+ CheckpointTuple,
21
+ )
22
+
23
+ _HAS_LANGGRAPH = True
24
+ except ImportError:
25
+ _HAS_LANGGRAPH = False
26
+ BaseCheckpointSaver = object # type: ignore
27
+
28
+
29
+ class SynapseCheckpointer(BaseCheckpointSaver): # type: ignore[misc]
30
+ """LangGraph-compatible checkpointer using Synapse for persistence.
31
+
32
+ Example::
33
+
34
+ from synapse import Synapse
35
+ from langchain_synapse import SynapseCheckpointer
36
+
37
+ syn = Synapse("./agent_state")
38
+ checkpointer = SynapseCheckpointer(synapse=syn)
39
+ graph = builder.compile(checkpointer=checkpointer)
40
+ """
41
+
42
+ def __init__(self, synapse: Optional[Synapse] = None, path: str = ":memory:"):
43
+ if _HAS_LANGGRAPH:
44
+ super().__init__()
45
+ self.synapse = synapse or Synapse(path)
46
+ self._index: Dict[str, list] = {}
47
+ self._rebuild_index()
48
+
49
+ def _rebuild_index(self) -> None:
50
+ all_memories = self.synapse.recall("", limit=10000)
51
+ for mem in all_memories:
52
+ meta = mem.metadata or {}
53
+ if meta.get("source") == "langgraph_checkpoint":
54
+ thread_id = meta.get("thread_id", "default")
55
+ checkpoint_id = meta.get("checkpoint_id", "")
56
+ self._index.setdefault(thread_id, []).append(
57
+ (checkpoint_id, mem.id, mem.created_at)
58
+ )
59
+ for thread_id in self._index:
60
+ self._index[thread_id].sort(key=lambda x: x[2])
61
+
62
+ def _make_checkpoint_id(self, thread_id: str) -> str:
63
+ count = len(self._index.get(thread_id, []))
64
+ return f"{thread_id}:{count}:{time.time()}"
65
+
66
+ def put(
67
+ self,
68
+ config: Dict[str, Any],
69
+ checkpoint: Dict[str, Any],
70
+ metadata: Optional[Dict[str, Any]] = None,
71
+ new_versions: Optional[Any] = None,
72
+ ) -> Dict[str, Any]:
73
+ configurable = config.get("configurable", {})
74
+ thread_id = configurable.get("thread_id", "default")
75
+ checkpoint_ns = configurable.get("checkpoint_ns", "")
76
+ checkpoint_id = self._make_checkpoint_id(thread_id)
77
+
78
+ content = json.dumps(checkpoint, default=str)
79
+ mem = self.synapse.remember(
80
+ content,
81
+ memory_type="fact",
82
+ metadata={
83
+ "source": "langgraph_checkpoint",
84
+ "thread_id": thread_id,
85
+ "checkpoint_ns": checkpoint_ns,
86
+ "checkpoint_id": checkpoint_id,
87
+ "checkpoint_metadata": json.dumps(metadata or {}, default=str),
88
+ },
89
+ deduplicate=False,
90
+ )
91
+ self._index.setdefault(thread_id, []).append(
92
+ (checkpoint_id, mem.id, mem.created_at)
93
+ )
94
+ return {
95
+ "configurable": {
96
+ "thread_id": thread_id,
97
+ "checkpoint_ns": checkpoint_ns,
98
+ "checkpoint_id": checkpoint_id,
99
+ }
100
+ }
101
+
102
+ def put_writes(
103
+ self,
104
+ config: Dict[str, Any],
105
+ writes: Sequence[Tuple[str, Any]],
106
+ task_id: str,
107
+ ) -> None:
108
+ configurable = config.get("configurable", {})
109
+ thread_id = configurable.get("thread_id", "default")
110
+ checkpoint_id = configurable.get("checkpoint_id", "")
111
+ content = json.dumps({"writes": writes, "task_id": task_id}, default=str)
112
+ self.synapse.remember(
113
+ content,
114
+ memory_type="fact",
115
+ metadata={
116
+ "source": "langgraph_writes",
117
+ "thread_id": thread_id,
118
+ "checkpoint_id": checkpoint_id,
119
+ "task_id": task_id,
120
+ },
121
+ deduplicate=False,
122
+ )
123
+
124
+ def get_tuple(self, config: Dict[str, Any]) -> Optional[Any]:
125
+ configurable = config.get("configurable", {})
126
+ thread_id = configurable.get("thread_id", "default")
127
+ checkpoint_id = configurable.get("checkpoint_id")
128
+ entries = self._index.get(thread_id, [])
129
+ if not entries:
130
+ return None
131
+
132
+ target = (
133
+ next((e for e in entries if e[0] == checkpoint_id), None)
134
+ if checkpoint_id
135
+ else entries[-1]
136
+ )
137
+ if not target:
138
+ return None
139
+
140
+ cp_id, mem_id, _ = target
141
+ memories = self.synapse.recall("", limit=10000)
142
+ mem = next((m for m in memories if m.id == mem_id), None)
143
+ if not mem:
144
+ return None
145
+
146
+ checkpoint = json.loads(mem.content)
147
+ meta = mem.metadata or {}
148
+ checkpoint_metadata = json.loads(meta.get("checkpoint_metadata", "{}"))
149
+
150
+ if _HAS_LANGGRAPH:
151
+ return CheckpointTuple(
152
+ config={
153
+ "configurable": {
154
+ "thread_id": thread_id,
155
+ "checkpoint_ns": meta.get("checkpoint_ns", ""),
156
+ "checkpoint_id": cp_id,
157
+ }
158
+ },
159
+ checkpoint=checkpoint,
160
+ metadata=checkpoint_metadata,
161
+ parent_config=None,
162
+ pending_writes=[],
163
+ )
164
+ return {"config": config, "checkpoint": checkpoint, "metadata": checkpoint_metadata}
165
+
166
+ def list(
167
+ self,
168
+ config: Optional[Dict[str, Any]] = None,
169
+ *,
170
+ filter: Optional[Dict[str, Any]] = None,
171
+ before: Optional[Dict[str, Any]] = None,
172
+ limit: Optional[int] = None,
173
+ ) -> Iterator[Any]:
174
+ if config:
175
+ thread_id = config.get("configurable", {}).get("thread_id", "default")
176
+ entries = self._index.get(thread_id, [])
177
+ else:
178
+ entries = [e for thread_entries in self._index.values() for e in thread_entries]
179
+ thread_id = "default"
180
+
181
+ entries.sort(key=lambda x: x[2], reverse=True)
182
+ if limit:
183
+ entries = entries[:limit]
184
+ for cp_id, mem_id, created_at in entries:
185
+ result = self.get_tuple(
186
+ {"configurable": {"thread_id": thread_id, "checkpoint_id": cp_id}}
187
+ )
188
+ if result:
189
+ yield result
@@ -0,0 +1,98 @@
1
+ """SynapseMemory — LangChain BaseMemory backed by Synapse.
2
+
3
+ Privacy-first: all memory stays local. No API calls for storage.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import Any, Dict, List, Optional
9
+
10
+ from langchain_core.memory import BaseMemory
11
+ from synapse import Synapse
12
+
13
+
14
+ class SynapseMemory(BaseMemory):
15
+ """LangChain-compatible memory backed by Synapse.
16
+
17
+ Stores conversation history and relevant facts in a local Synapse
18
+ database. Recall is semantic — not just last-N messages.
19
+
20
+ Example::
21
+
22
+ from synapse import Synapse
23
+ from langchain_synapse import SynapseMemory
24
+
25
+ syn = Synapse("./agent_memory")
26
+ memory = SynapseMemory(synapse=syn)
27
+ """
28
+
29
+ synapse: Any # Synapse instance
30
+ memory_key: str = "history"
31
+ input_key: Optional[str] = None
32
+ output_key: Optional[str] = None
33
+ recall_limit: int = 10
34
+ store_inputs: bool = True
35
+ store_outputs: bool = True
36
+ return_messages: bool = False
37
+
38
+ class Config:
39
+ arbitrary_types_allowed = True
40
+
41
+ def __init__(self, synapse: Optional[Synapse] = None, path: str = ":memory:", **kwargs: Any):
42
+ super().__init__(synapse=synapse or Synapse(path), **kwargs)
43
+
44
+ @property
45
+ def memory_variables(self) -> List[str]:
46
+ return [self.memory_key]
47
+
48
+ def load_memory_variables(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
49
+ """Recall relevant memories based on the current input."""
50
+ context_parts = [v for v in inputs.values() if isinstance(v, str)]
51
+ context = " ".join(context_parts) if context_parts else ""
52
+
53
+ memories = self.synapse.recall(context, limit=self.recall_limit)
54
+
55
+ if self.return_messages:
56
+ from langchain_core.messages import AIMessage, HumanMessage
57
+
58
+ messages = []
59
+ for mem in memories:
60
+ role = (mem.metadata or {}).get("role", "human")
61
+ if role == "ai":
62
+ messages.append(AIMessage(content=mem.content))
63
+ else:
64
+ messages.append(HumanMessage(content=mem.content))
65
+ return {self.memory_key: messages}
66
+
67
+ formatted = "\n".join(f"- {m.content}" for m in memories)
68
+ return {self.memory_key: formatted}
69
+
70
+ def save_context(self, inputs: Dict[str, Any], outputs: Dict[str, str]) -> None:
71
+ """Store the conversation turn in Synapse."""
72
+ if self.store_inputs:
73
+ input_key = self.input_key or next(iter(inputs), None)
74
+ if input_key and input_key in inputs:
75
+ value = inputs[input_key]
76
+ if isinstance(value, str) and value.strip():
77
+ self.synapse.remember(
78
+ value,
79
+ memory_type="observation",
80
+ metadata={"role": "human", "source": "langchain"},
81
+ )
82
+
83
+ if self.store_outputs:
84
+ output_key = self.output_key or next(iter(outputs), None)
85
+ if output_key and output_key in outputs:
86
+ value = outputs[output_key]
87
+ if isinstance(value, str) and value.strip():
88
+ self.synapse.remember(
89
+ value,
90
+ memory_type="observation",
91
+ metadata={"role": "ai", "source": "langchain"},
92
+ )
93
+
94
+ def clear(self) -> None:
95
+ """Clear all memories."""
96
+ all_memories = self.synapse.recall("", limit=10000)
97
+ for mem in all_memories:
98
+ self.synapse.forget(mem.id)
@@ -0,0 +1,78 @@
1
+ """SynapseRetriever — LangChain BaseRetriever backed by Synapse.
2
+
3
+ Privacy-first: all retrieval is local. No vector DB API calls.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import Any, Dict, List, Optional
9
+
10
+ from langchain_core.callbacks import CallbackManagerForRetrieverRun
11
+ from langchain_core.documents import Document
12
+ from langchain_core.retrievers import BaseRetriever
13
+ from synapse import Synapse
14
+
15
+
16
+ class SynapseRetriever(BaseRetriever):
17
+ """LangChain-compatible retriever using Synapse's semantic recall.
18
+
19
+ Synapse combines BM25, concept graphs, and optional local embeddings
20
+ for retrieval — all running locally with zero external API calls.
21
+
22
+ Example::
23
+
24
+ from synapse import Synapse
25
+ from langchain_synapse import SynapseRetriever
26
+
27
+ syn = Synapse("./knowledge_base")
28
+ syn.remember("Python was created by Guido van Rossum")
29
+
30
+ retriever = SynapseRetriever(synapse=syn, k=5)
31
+ docs = retriever.invoke("Who created Python?")
32
+ """
33
+
34
+ synapse: Any # Synapse instance
35
+ k: int = 5
36
+ memory_type: Optional[str] = None
37
+ min_strength: float = 0.01
38
+ metadata_filter: Dict[str, Any] = {}
39
+
40
+ class Config:
41
+ arbitrary_types_allowed = True
42
+
43
+ def __init__(self, synapse: Optional[Synapse] = None, path: str = ":memory:", **kwargs: Any):
44
+ super().__init__(synapse=synapse or Synapse(path), **kwargs)
45
+
46
+ def _get_relevant_documents(
47
+ self, query: str, *, run_manager: CallbackManagerForRetrieverRun
48
+ ) -> List[Document]:
49
+ """Retrieve relevant documents from Synapse."""
50
+ memories = self.synapse.recall(
51
+ query,
52
+ limit=self.k,
53
+ memory_type=self.memory_type,
54
+ min_strength=self.min_strength,
55
+ )
56
+
57
+ if self.metadata_filter:
58
+ memories = [
59
+ m for m in memories
60
+ if all(
61
+ (m.metadata or {}).get(k) == v
62
+ for k, v in self.metadata_filter.items()
63
+ )
64
+ ]
65
+
66
+ return [
67
+ Document(
68
+ page_content=mem.content,
69
+ metadata={
70
+ "memory_id": mem.id,
71
+ "memory_type": mem.memory_type,
72
+ "strength": mem.effective_strength,
73
+ "created_at": mem.created_at,
74
+ **(mem.metadata or {}),
75
+ },
76
+ )
77
+ for mem in memories
78
+ ]
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "langchain-synapse"
7
+ version = "0.1.0"
8
+ description = "An integration package connecting Synapse memory and LangChain. Privacy-first, zero API calls, pure Python."
9
+ license = { text = "MIT" }
10
+ readme = "README.md"
11
+ classifiers = [
12
+ "Development Status :: 4 - Beta",
13
+ "Intended Audience :: Developers",
14
+ "License :: OSI Approved :: MIT License",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.10",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
21
+ ]
22
+ requires-python = ">=3.10"
23
+ dependencies = [
24
+ "langchain-core>=0.3.0,<1.0.0",
25
+ "synapse-ai-memory>=0.3.0",
26
+ ]
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/raghuram369/synapse"
30
+ Repository = "https://github.com/raghuram369/synapse"
31
+ Issues = "https://github.com/raghuram369/synapse/issues"
32
+
33
+ [dependency-groups]
34
+ test = [
35
+ "pytest>=7.3.0,<9.0.0",
36
+ "pytest-asyncio>=0.21.1,<1.0.0",
37
+ "langchain-core",
38
+ ]
39
+ dev = ["langchain-core"]
File without changes
@@ -0,0 +1,43 @@
1
+ """Tests for SynapseChatMessageHistory."""
2
+
3
+ from langchain_core.messages import AIMessage, HumanMessage
4
+ from synapse import Synapse
5
+ from langchain_synapse import SynapseChatMessageHistory
6
+
7
+
8
+ def test_add_and_retrieve_messages():
9
+ syn = Synapse(":memory:")
10
+ history = SynapseChatMessageHistory(synapse=syn, session_id="test")
11
+ history.add_user_message("Hello")
12
+ history.add_ai_message("Hi there!")
13
+ messages = history.messages
14
+ assert len(messages) == 2
15
+ assert isinstance(messages[0], HumanMessage)
16
+ assert isinstance(messages[1], AIMessage)
17
+
18
+
19
+ def test_search():
20
+ syn = Synapse(":memory:")
21
+ history = SynapseChatMessageHistory(synapse=syn, session_id="test")
22
+ history.add_user_message("I love Italian food")
23
+ history.add_user_message("The weather is nice today")
24
+ results = history.search("cuisine preferences")
25
+ assert len(results) > 0
26
+
27
+
28
+ def test_clear():
29
+ syn = Synapse(":memory:")
30
+ history = SynapseChatMessageHistory(synapse=syn, session_id="test")
31
+ history.add_user_message("Hello")
32
+ history.clear()
33
+ assert len(history.messages) == 0
34
+
35
+
36
+ def test_session_isolation():
37
+ syn = Synapse(":memory:")
38
+ h1 = SynapseChatMessageHistory(synapse=syn, session_id="s1")
39
+ h2 = SynapseChatMessageHistory(synapse=syn, session_id="s2")
40
+ h1.add_user_message("Session 1 message")
41
+ h2.add_user_message("Session 2 message")
42
+ assert len(h1.messages) == 1
43
+ assert len(h2.messages) == 1
@@ -0,0 +1,31 @@
1
+ """Tests for SynapseCheckpointer."""
2
+
3
+ from synapse import Synapse
4
+ from langchain_synapse.checkpointer import SynapseCheckpointer
5
+
6
+
7
+ def test_put_and_get():
8
+ syn = Synapse(":memory:")
9
+ cp = SynapseCheckpointer(synapse=syn)
10
+ config = {"configurable": {"thread_id": "t1"}}
11
+ checkpoint = {"v": 1, "ts": "2024-01-01", "channel_values": {"messages": []}}
12
+
13
+ result_config = cp.put(config, checkpoint, metadata={"step": 0})
14
+ assert "configurable" in result_config
15
+ assert result_config["configurable"]["thread_id"] == "t1"
16
+
17
+ # Retrieve
18
+ tup = cp.get_tuple(config)
19
+ assert tup is not None
20
+
21
+
22
+ def test_list_checkpoints():
23
+ syn = Synapse(":memory:")
24
+ cp = SynapseCheckpointer(synapse=syn)
25
+ config = {"configurable": {"thread_id": "t1"}}
26
+
27
+ cp.put(config, {"v": 1, "ts": "1"}, metadata={"step": 0})
28
+ cp.put(config, {"v": 1, "ts": "2"}, metadata={"step": 1})
29
+
30
+ items = list(cp.list(config))
31
+ assert len(items) == 2
@@ -0,0 +1,36 @@
1
+ """Tests for SynapseMemory."""
2
+
3
+ from synapse import Synapse
4
+ from langchain_synapse import SynapseMemory
5
+
6
+
7
+ def test_memory_variables():
8
+ mem = SynapseMemory(synapse=Synapse(":memory:"))
9
+ assert mem.memory_variables == ["history"]
10
+
11
+
12
+ def test_save_and_load():
13
+ syn = Synapse(":memory:")
14
+ mem = SynapseMemory(synapse=syn)
15
+ mem.save_context({"input": "I like pizza"}, {"output": "Great choice!"})
16
+ result = mem.load_memory_variables({"input": "food preferences"})
17
+ assert "history" in result
18
+ assert len(result["history"]) > 0
19
+
20
+
21
+ def test_clear():
22
+ syn = Synapse(":memory:")
23
+ mem = SynapseMemory(synapse=syn)
24
+ mem.save_context({"input": "test"}, {"output": "response"})
25
+ mem.clear()
26
+ result = mem.load_memory_variables({"input": "test"})
27
+ assert result["history"] == ""
28
+
29
+
30
+ def test_return_messages():
31
+ syn = Synapse(":memory:")
32
+ mem = SynapseMemory(synapse=syn, return_messages=True)
33
+ mem.save_context({"input": "hello"}, {"output": "hi there"})
34
+ result = mem.load_memory_variables({"input": "greeting"})
35
+ messages = result["history"]
36
+ assert len(messages) > 0
@@ -0,0 +1,23 @@
1
+ """Tests for SynapseRetriever."""
2
+
3
+ from langchain_core.documents import Document
4
+ from synapse import Synapse
5
+ from langchain_synapse import SynapseRetriever
6
+
7
+
8
+ def test_retrieve_documents():
9
+ syn = Synapse(":memory:")
10
+ syn.remember("Python was created by Guido van Rossum")
11
+ syn.remember("JavaScript was created by Brendan Eich")
12
+
13
+ retriever = SynapseRetriever(synapse=syn, k=5)
14
+ docs = retriever.invoke("Who created Python?")
15
+ assert len(docs) > 0
16
+ assert all(isinstance(d, Document) for d in docs)
17
+
18
+
19
+ def test_empty_retrieval():
20
+ syn = Synapse(":memory:")
21
+ retriever = SynapseRetriever(synapse=syn, k=5)
22
+ docs = retriever.invoke("anything")
23
+ assert docs == []