memoryagent-lib 0.1.1__py3-none-any.whl

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.
memoryagent/utils.py ADDED
@@ -0,0 +1,35 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from typing import Iterable, List, Set
5
+
6
+
7
+ _WORD_RE = re.compile(r"[a-zA-Z0-9']+")
8
+
9
+
10
+ def tokenize(text: str) -> List[str]:
11
+ return [t.lower() for t in _WORD_RE.findall(text or "")]
12
+
13
+
14
+ def unique_tokens(text: str) -> Set[str]:
15
+ return set(tokenize(text))
16
+
17
+
18
+ def safe_div(numerator: float, denominator: float) -> float:
19
+ return numerator / denominator if denominator else 0.0
20
+
21
+
22
+ def clamp(value: float, low: float = 0.0, high: float = 1.0) -> float:
23
+ return max(low, min(high, value))
24
+
25
+
26
+ def hash_embed(text: str, dim: int) -> List[float]:
27
+ tokens = tokenize(text)
28
+ if dim <= 0:
29
+ raise ValueError("dim must be positive")
30
+ vector = [0.0] * dim
31
+ for token in tokens:
32
+ idx = hash(token) % dim
33
+ vector[idx] += 1.0
34
+ norm = sum(v * v for v in vector) ** 0.5 or 1.0
35
+ return [v / norm for v in vector]
memoryagent/workers.py ADDED
@@ -0,0 +1,169 @@
1
+ from __future__ import annotations
2
+
3
+ from collections import Counter
4
+ from typing import List, Tuple
5
+
6
+ from memoryagent.config import MemorySystemConfig
7
+ from memoryagent.indexers import EpisodicIndexer
8
+ from memoryagent.models import MemoryItem, MemoryType, StorageTier, utc_now
9
+ from memoryagent.storage.base import MetadataStore, ObjectStore, VectorIndex
10
+
11
+
12
+ class ConsolidationWorker:
13
+ def __init__(
14
+ self,
15
+ metadata_store: MetadataStore,
16
+ vector_index: VectorIndex,
17
+ config: MemorySystemConfig,
18
+ ) -> None:
19
+ self.metadata_store = metadata_store
20
+ self.vector_index = vector_index
21
+ self.config = config
22
+ self.indexer = EpisodicIndexer(vector_index)
23
+
24
+ async def run_once(self, owner: str) -> List[MemoryItem]:
25
+ items = await self.metadata_store.list_by_owner(owner)
26
+ working = [item for item in items if item.type == MemoryType.WORKING and item.tier == StorageTier.HOT]
27
+ perceptual = [item for item in items if item.type == MemoryType.PERCEPTUAL and item.tier == StorageTier.HOT]
28
+
29
+ new_items: List[MemoryItem] = []
30
+
31
+ if working:
32
+ summary = " | ".join([item.summary for item in working[:5]])
33
+ new_items.append(
34
+ MemoryItem(
35
+ type=MemoryType.EPISODIC,
36
+ owner=owner,
37
+ summary=f"Session summary: {summary}",
38
+ tags=["session-summary"],
39
+ confidence=0.6,
40
+ )
41
+ )
42
+
43
+ if perceptual:
44
+ snippets = [item.summary for item in perceptual[: self.config.consolidation.perceptual_summary_limit]]
45
+ new_items.append(
46
+ MemoryItem(
47
+ type=MemoryType.EPISODIC,
48
+ owner=owner,
49
+ summary=f"Perceptual highlights: {' | '.join(snippets)}",
50
+ tags=["perceptual-summary"],
51
+ confidence=0.55,
52
+ )
53
+ )
54
+
55
+ tag_counts = Counter()
56
+ for item in working + perceptual:
57
+ tag_counts.update(item.tags)
58
+
59
+ for tag, count in tag_counts.items():
60
+ if count >= self.config.consolidation.semantic_min_count:
61
+ new_items.append(
62
+ MemoryItem(
63
+ type=MemoryType.SEMANTIC,
64
+ owner=owner,
65
+ summary=f"Observed recurring tag: {tag}",
66
+ tags=[tag, "derived"],
67
+ confidence=0.65,
68
+ stability=0.6,
69
+ )
70
+ )
71
+
72
+ for item in new_items:
73
+ await self.metadata_store.upsert(item)
74
+ await self.indexer.index_hot(item)
75
+
76
+ return new_items
77
+
78
+
79
+ class ArchiverWorker:
80
+ def __init__(
81
+ self,
82
+ metadata_store: MetadataStore,
83
+ object_store: ObjectStore,
84
+ vector_index: VectorIndex,
85
+ ) -> None:
86
+ self.metadata_store = metadata_store
87
+ self.object_store = object_store
88
+ self.vector_index = vector_index
89
+ self.indexer = EpisodicIndexer(vector_index)
90
+
91
+ async def run_once(self, owner: str) -> List[MemoryItem]:
92
+ items = await self.metadata_store.list_by_owner(owner)
93
+ to_archive = [item for item in items if item.tier == StorageTier.HOT and item.type != MemoryType.WORKING]
94
+
95
+ archived: List[MemoryItem] = []
96
+ for item in to_archive:
97
+ date_path = item.created_at.strftime("%Y/%m/%d")
98
+ key = f"{owner}/{date_path}/daily_notes"
99
+ payload = {
100
+ "id": str(item.id),
101
+ "summary": item.summary,
102
+ "content": item.content,
103
+ "tags": item.tags,
104
+ "type": item.type.value,
105
+ "owner": item.owner,
106
+ "created_at": item.created_at.isoformat(),
107
+ }
108
+ if hasattr(self.object_store, "append"):
109
+ object_path = await self.object_store.append(key, payload)
110
+ else:
111
+ object_path = await self.object_store.put(key, payload)
112
+ item.pointer["object_key"] = object_path
113
+ item.pointer["archive_key"] = key
114
+ item.tier = StorageTier.COLD
115
+ item.updated_at = utc_now()
116
+ await self.metadata_store.upsert(item)
117
+ await self.indexer.index_archive(item)
118
+ archived.append(item)
119
+ return archived
120
+
121
+
122
+ class RehydratorWorker:
123
+ def __init__(
124
+ self,
125
+ metadata_store: MetadataStore,
126
+ vector_index: VectorIndex,
127
+ access_threshold: int = 3,
128
+ ) -> None:
129
+ self.metadata_store = metadata_store
130
+ self.vector_index = vector_index
131
+ self.access_threshold = access_threshold
132
+ self._access_counts = {}
133
+
134
+ async def record_access(self, item_id) -> None:
135
+ item_id = str(item_id)
136
+ self._access_counts[item_id] = self._access_counts.get(item_id, 0) + 1
137
+
138
+ async def run_once(self, owner: str) -> List[MemoryItem]:
139
+ items = await self.metadata_store.list_by_owner(owner)
140
+ warmed: List[MemoryItem] = []
141
+ for item in items:
142
+ if item.tier != StorageTier.COLD:
143
+ continue
144
+ count = self._access_counts.get(str(item.id), 0)
145
+ if count >= self.access_threshold:
146
+ item.tier = StorageTier.HOT
147
+ item.updated_at = utc_now()
148
+ await self.metadata_store.upsert(item)
149
+ await self.vector_index.upsert(
150
+ item.id,
151
+ text=item.text(),
152
+ metadata={"owner": item.owner, "tier": StorageTier.HOT.value, "type": item.type.value, "item": item},
153
+ )
154
+ warmed.append(item)
155
+ return warmed
156
+
157
+
158
+ class Compactor:
159
+ def __init__(self, metadata_store: MetadataStore) -> None:
160
+ self.metadata_store = metadata_store
161
+
162
+ async def run_once(self, owner: str) -> List[MemoryItem]:
163
+ items = await self.metadata_store.list_by_owner(owner)
164
+ removed: List[MemoryItem] = []
165
+ for item in items:
166
+ if item.is_expired():
167
+ await self.metadata_store.delete(item.id)
168
+ removed.append(item)
169
+ return removed
@@ -0,0 +1,186 @@
1
+ Metadata-Version: 2.4
2
+ Name: memoryagent-lib
3
+ Version: 0.1.1
4
+ Summary: Add your description here
5
+ Author-email: Jiawei Zheng <jw.zhengai@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/jia-wei-zheng/MemoryAgent
8
+ Project-URL: Repository, https://github.com/jia-wei-zheng/MemoryAgent
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: python-dotenv>=0.9.9
12
+ Requires-Dist: openai>=2.16.0
13
+ Requires-Dist: pydantic>=2.0
14
+ Requires-Dist: sqlite-vec>=0.1.6
15
+
16
+ #
17
+
18
+ <div align="center">
19
+ <img src="https://raw.githubusercontent.com/jia-wei-zheng/MemoryAgent/refs/heads/master/memoryagent_logo.jpg?token=GHSAT0AAAAAADSXDGNDXIWZF42E6S3D2XPG2MDPSDQ" alt="MemoryAgent" width="500">
20
+ <h1>MemoryAgent: An Open, Modular Memory Framework for Agents (Beta)</h1>
21
+ </div>
22
+
23
+ MemoryAgent is a reusable memory framework for LLM-based agent systems. It provides tiered memory (working, episodic, semantic, perceptual), hot/cold storage, archive indexing, confidence-based retrieval escalation, and optional local vector search via sqlite-vec.
24
+
25
+ ## Highlights
26
+ - **Tiered memory**: working (TTL), episodic, semantic, perceptual
27
+ - **Storage tiers**: hot metadata (SQLite + sqlite-vec), cold archive (filesystem), archive index (vector index)
28
+ - **Memory retrieval pipeline**: hot -> archive -> cold hydration with rerank + context packaging
29
+ - **Local mode**: SQLite + sqlite-vec (optional) + filesystem
30
+ - **Async-friendly** with sync convenience methods
31
+
32
+ ## Project Layout
33
+ ```
34
+ memoryagent/
35
+ config.py # Default system settings and retrieval thresholds
36
+ models.py # Pydantic data models for memory items, queries, bundles
37
+ system.py # MemorySystem entry point and wiring
38
+ retrieval.py # Retrieval orchestration and reranking
39
+ confidence.py # Confidence scoring components
40
+ policy.py # Conversation + routing policies
41
+ indexers.py # Episodic/semantic/perceptual indexers
42
+ workers.py # Consolidation, archiving, rehydration, compaction
43
+ storage/
44
+ base.py # Storage adapter interfaces
45
+ in_memory.py # Simple in-memory vector + graph stores
46
+ local_disk.py # SQLite metadata/features + sqlite-vec + file object store
47
+ examples/
48
+ minimal.py # Basic usage example
49
+ openai_agent.py # CLI OpenAI agent with memory retrieval
50
+ memory_api_server.py # Local API for memory + chat
51
+ memory_viz.html # Web UI for chat + memory visualization
52
+ ```
53
+
54
+ ## Installation
55
+ Python 3.10+ required.
56
+
57
+ Development (sync deps from `uv.lock`):
58
+ ```bash
59
+ uv sync
60
+ ```
61
+
62
+ Use as a dependency:
63
+ ```bash
64
+ uv add memoryagent-lib
65
+ # or
66
+ pip install memoryagent-lib
67
+ ```
68
+
69
+ Optional extras:
70
+ ```bash
71
+ uv add openai sqlite-vec
72
+ # or
73
+ pip install openai sqlite-vec
74
+ ```
75
+
76
+ ## Quick Start
77
+ ```python
78
+ from memoryagent import MemoryEvent, MemorySystem
79
+
80
+ memory = MemorySystem()
81
+ owner = "user-001"
82
+
83
+ memory.write(
84
+ MemoryEvent(
85
+ content="User prefers concise summaries about climate policy.",
86
+ type="semantic",
87
+ owner=owner,
88
+ tags=["preference", "summary"],
89
+ confidence=0.7,
90
+ stability=0.8,
91
+ )
92
+ )
93
+
94
+ bundle = memory.retrieve("What policy topics did we cover?", owner=owner)
95
+ print(bundle.confidence.total)
96
+ for block in bundle.blocks:
97
+ print(block.text)
98
+
99
+ memory.flush(owner)
100
+ ```
101
+
102
+ ## Enable sqlite-vec (Local Vector Search)
103
+ ```python
104
+ from memoryagent import MemorySystem, MemorySystemConfig
105
+
106
+ config = MemorySystemConfig(
107
+ use_sqlite_vec=True,
108
+ vector_dim=1536, # match your embedding model
109
+ )
110
+
111
+ memory = MemorySystem(config=config)
112
+ ```
113
+
114
+ If sqlite-vec cannot be auto-loaded, set an explicit path:
115
+ ```python
116
+ from pathlib import Path
117
+ from memoryagent import MemorySystemConfig
118
+
119
+ config = MemorySystemConfig(
120
+ use_sqlite_vec=True,
121
+ vector_dim=1536,
122
+ sqlite_vec_extension_path=Path("/path/to/sqlite_vec.dylib"),
123
+ )
124
+ ```
125
+
126
+ ## Policies
127
+ ### Conversation storage policy
128
+ `HeuristicMemoryPolicy` decides whether a turn should be stored and whether it becomes episodic or semantic memory.
129
+
130
+ ### Routing policy
131
+ `MemoryRoutingPolicy` decides where a memory should be written:
132
+ - **Hot** metadata store
133
+ - **Vector index**
134
+ - **Feature store** (perceptual)
135
+ - **Cold** archive (via workers)
136
+
137
+ ## Background Workers
138
+ - `ConsolidationWorker`: working → episodic/semantic
139
+ - `ArchiverWorker`: hot → cold + archive index
140
+ - `RehydratorWorker`: cold → hot (based on access)
141
+ - `Compactor`: cleanup/TTL
142
+
143
+ ## Examples
144
+ ### OpenAI Agent (CLI)
145
+ ```bash
146
+ python -m memoryagent.examples.openai_agent
147
+ ```
148
+ - Uses OpenAI responses + embeddings.
149
+ - Stores session transcript as a single working memory item.
150
+
151
+ ### Memory Visualization + API
152
+ Start the API server:
153
+ ```bash
154
+ python -m memoryagent.examples.memory_api_server
155
+ ```
156
+ Open in browser:
157
+ ```
158
+ http://127.0.0.1:8000/memory_viz.html
159
+ ```
160
+
161
+ An example (System records semantic memory and updating working memory):
162
+
163
+ ![Screenshot](https://raw.githubusercontent.com/jia-wei-zheng/MemoryAgent/master/Memory%20Agent%20_%20Live%20Console.jpeg?token=GHSAT0AAAAAADSXDGNDWOWFKKV777VMZM6U2MDPSTQ)
164
+
165
+
166
+ The page calls:
167
+ - `GET /api/memory?owner=user-001`
168
+ - `POST /api/chat`
169
+
170
+ ## Data Stores
171
+ - **Hot metadata**: `.memoryagent_hot.sqlite`
172
+ - **Vector index**: `.memoryagent_vectors.sqlite` (sqlite-vec)
173
+ - **Features**: `.memoryagent_features.sqlite`
174
+ - **Cold archive**: `.memoryagent_cold/records/<owner>/YYYY/MM/DD/daily_notes.json`
175
+
176
+ ## Configuration
177
+ See `memoryagent/config.py` for defaults:
178
+ - `working_ttl_seconds`
179
+ - `retrieval_plan` thresholds and budgets
180
+ - `use_sqlite_vec`, `vector_dim`, `sqlite_vec_extension_path`
181
+
182
+ ## Notes
183
+ - Working memory is stored as a single session transcript (updated each turn).
184
+ - Episodic/semantic memories are candidates for cold archive.
185
+
186
+ ## License
@@ -0,0 +1,22 @@
1
+ memoryagent/__init__.py,sha256=WUv2YjbJzU4lpHIVPDLTrv3wglraPJh6Ky7P9f_r8OU,743
2
+ memoryagent/confidence.py,sha256=bEBrQFj7nTt7xIaFc21BqUaBQOy3DoIVpwTsBUysv54,2626
3
+ memoryagent/config.py,sha256=IO_aAkNP3kq0BukIEFsc3-CkcRlYhlDb7QG5e8lf3Tk,1334
4
+ memoryagent/consolidation.py,sha256=jG5AJr9PNxeqdPi3kD4bbj51YZkrxE4-ZSSTNCnN6Yc,123
5
+ memoryagent/indexers.py,sha256=I2Sv-jyWqs5K83BIXPvSDp9_Z18xClX8QedJyt3ZJzA,2030
6
+ memoryagent/models.py,sha256=mOt-rX8wlzT_n4JsvTDd6iKeJW-bdPp8VGFrb61OgdQ,4185
7
+ memoryagent/policy.py,sha256=doZbUIBAwQVr4BhPjcAtDs_GAM35Z04RYDnc3zf2AJo,6032
8
+ memoryagent/retrieval.py,sha256=PAvaHXAHxk6yXn5iZDR6t39UODwOciWNQimPredbtU4,6124
9
+ memoryagent/system.py,sha256=Dw-t8BXPs1TVNcKDvwavJlNwR95QErulX4c_fy-IyQc,7758
10
+ memoryagent/utils.py,sha256=PZ5AaZwUZDNxj64g70UTXK9ynLtGLeSiHpp8GCmbTIs,883
11
+ memoryagent/workers.py,sha256=2TrwovaAx_NZhcPCwsl-FpLwWCmFC0HfPzHQkJsKO9U,6235
12
+ memoryagent/examples/export_memory.py,sha256=_MEIeGNhXOe5gz9WYJZIl6Kzvdo-L1uuYx_CNJVY-L0,3262
13
+ memoryagent/examples/memory_api_server.py,sha256=AhZ0vynfBzYlUC1-kVkL5u8OHIolGp0Im_fhUQGb2zY,7882
14
+ memoryagent/examples/minimal.py,sha256=KjduPgOIOCy0B27LPxLlMwsEko19-piSlrZOPgv6pcc,1154
15
+ memoryagent/examples/openai_agent.py,sha256=u31uMM7SM-RYSotail49hs5CpRy01DTyrKOLADkNY8M,4577
16
+ memoryagent/storage/base.py,sha256=JYT0HcGIwUz-uydpy9huQ5ABK3G8XshDE6aM3PLpG2s,2453
17
+ memoryagent/storage/in_memory.py,sha256=-AfeYc9TO1EtD59vd2_JTZdJMmTlQYAYQK7fcpKhYAk,3373
18
+ memoryagent/storage/local_disk.py,sha256=EejHxueMmjL7PPPVVl6p8nNmrvJEdxyXQNORly9Nzow,15768
19
+ memoryagent_lib-0.1.1.dist-info/METADATA,sha256=awFooSFKAoQEPg8ZogIRpQ_uAxYh3U9E1mCwvAmnDho,5710
20
+ memoryagent_lib-0.1.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
21
+ memoryagent_lib-0.1.1.dist-info/top_level.txt,sha256=0svscYmHfWNY9RQMcUEEmE5I6ksCYfy4wR7P-CLrcIU,12
22
+ memoryagent_lib-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ memoryagent