retainr 0.2.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 @@
1
+ HF_TOKEN=your_token_here
@@ -0,0 +1,34 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master, main]
6
+ pull_request:
7
+ branches: [master, main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v4
21
+ with:
22
+ version: "latest"
23
+
24
+ - name: Set up Python ${{ matrix.python-version }}
25
+ run: uv python install ${{ matrix.python-version }}
26
+
27
+ - name: Install dependencies
28
+ run: uv sync --all-extras --dev
29
+
30
+ - name: Run tests
31
+ run: uv run pytest tests/ -v
32
+
33
+ - name: Lint
34
+ run: uv run ruff check memoryos/
@@ -0,0 +1,12 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+ .env
12
+ .memory.db
@@ -0,0 +1 @@
1
+ 3.12
retainr-0.2.0/LICENSE ADDED
File without changes
retainr-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,184 @@
1
+ Metadata-Version: 2.4
2
+ Name: retainr
3
+ Version: 0.2.0
4
+ Summary: Persistent, queryable AI memory for any Python app — local-first, zero API cost
5
+ Project-URL: Homepage, https://github.com/Devarajan8/memoryos
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.10
9
+ Requires-Dist: faiss-cpu>=1.8.0
10
+ Requires-Dist: groq>=1.2.0
11
+ Requires-Dist: sentence-transformers>=2.7.0
12
+ Description-Content-Type: text/markdown
13
+
14
+ # memoryos
15
+
16
+ [![CI](https://github.com/Devarajan8/memoryos/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/Devarajan8/memoryos/actions)
17
+
18
+ > Persistent, queryable memory for any Python AI app — local-first, zero API cost.
19
+
20
+ [![PyPI version](https://badge.fury.io/py/memoryos-official.svg)](https://pypi.org/project/memoryos-official/)
21
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
22
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
23
+
24
+ ---
25
+
26
+ ## What is it?
27
+
28
+ `memoryos` gives any AI app a long-term memory layer — without needing an API key, a cloud service, or a database server.
29
+
30
+ Store things. Recall them semantically. Forget them. All locally.
31
+
32
+ ```python
33
+ from memoryos import Memory
34
+
35
+ mem = Memory(user_id="arjun")
36
+
37
+ mem.remember("I prefer dark mode and use VS Code")
38
+ mem.remember("Currently building a job application assistant")
39
+ mem.remember("I like concise answers over long explanations")
40
+
41
+ results = mem.recall("what tools does the user prefer?")
42
+ for r in results:
43
+ print(f"[{r['score']:.3f}] {r['text']}")
44
+ ```
45
+
46
+ ```
47
+ [0.412] I prefer dark mode and use VS Code
48
+ [0.289] I like concise answers over long explanations
49
+ ```
50
+
51
+ ## CLI Demo
52
+
53
+ ![alt text](image.png)
54
+
55
+ ### Stats
56
+
57
+ ![alt text](image-1.png)
58
+
59
+ ---
60
+
61
+ ## Install
62
+
63
+ ```bash
64
+ pip install memoryos-official
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Features
70
+
71
+ - **Semantic recall** — finds memories by meaning, not exact keywords
72
+ - **Memory decay** — old memories fade in relevance automatically
73
+ - **Importance scoring** — weight memories by how significant they are
74
+ - **Tags** — organize memories into namespaces
75
+ - **Forget & clear** — GDPR-friendly deletion
76
+ - **Local-first** — uses FAISS + SQLite, no external services
77
+ - **Zero cost** — no API keys, no cloud, runs on your machine
78
+
79
+ ---
80
+
81
+ ## API Reference
82
+
83
+ ### `Memory(user_id, db_path)`
84
+
85
+ ```python
86
+ mem = Memory(user_id="alice", db_path="memory.db")
87
+ ```
88
+
89
+ | Param | Default | Description |
90
+ | --------- | ------------- | ----------------------------------------------------- |
91
+ | `user_id` | `"default"` | Isolates memories per user |
92
+ | `db_path` | `"memory.db"` | SQLite file path. Use `":memory:"` for in-RAM (tests) |
93
+
94
+ ---
95
+
96
+ ### `remember(text, tags, importance, decay_days)`
97
+
98
+ Store a memory.
99
+
100
+ ```python
101
+ mid = mem.remember(
102
+ "User just got promoted to SDE-2",
103
+ tags=["career", "milestone"],
104
+ importance=0.9, # 0.0–1.0, default 0.5
105
+ decay_days=90, # score halves every 90 days, 0 = no decay
106
+ )
107
+ ```
108
+
109
+ Returns: `str` — the memory ID (use it to `forget()` later)
110
+
111
+ ---
112
+
113
+ ### `recall(query, top_k)`
114
+
115
+ Find the most semantically relevant memories.
116
+
117
+ ```python
118
+ results = mem.recall("what is the user's job status?", top_k=3)
119
+ # [{"id": ..., "text": ..., "score": 0.87, "tags": [...], ...}]
120
+ ```
121
+
122
+ ---
123
+
124
+ ### `forget(memory_id)`
125
+
126
+ Delete a specific memory.
127
+
128
+ ```python
129
+ mem.forget(mid)
130
+ ```
131
+
132
+ ---
133
+
134
+ ### `clear()`
135
+
136
+ Wipe all memories for this user.
137
+
138
+ ```python
139
+ mem.clear()
140
+ ```
141
+
142
+ ---
143
+
144
+ ### `export(filepath)`
145
+
146
+ Backup all memories to JSON.
147
+
148
+ ```python
149
+ mem.export("my_memories.json")
150
+ ```
151
+
152
+ ---
153
+
154
+ ## How it works
155
+
156
+ 1. **`remember()`** → text is embedded using `sentence-transformers` (all-MiniLM-L6-v2, runs locally) → vector stored in FAISS, metadata in SQLite
157
+ 2. **`recall()`** → query is embedded → FAISS finds top-k nearest vectors → SQLite returns the original text
158
+ 3. **Scoring** → final score = cosine similarity × (0.7 + 0.3 × decayed importance)
159
+
160
+ ---
161
+
162
+ ## Why not mem0 / Zep / etc.?
163
+
164
+ | | memoryos | mem0 | Zep |
165
+ | -------------- | -------- | ---------- | ---------- |
166
+ | Local-first | ✅ | ❌ | ❌ |
167
+ | API key needed | ❌ | ✅ | ✅ |
168
+ | Cost | Free | Paid tiers | Paid tiers |
169
+ | Memory decay | ✅ | ❌ | ❌ |
170
+ | `pip install` | ✅ | ✅ | ✅ |
171
+
172
+ ---
173
+
174
+ ## Stack
175
+
176
+ - [`sentence-transformers`](https://www.sbert.net/) — local text embeddings
177
+ - [`faiss-cpu`](https://github.com/facebookresearch/faiss) — vector similarity search
178
+ - `sqlite3` — metadata storage (built into Python)
179
+
180
+ ---
181
+
182
+ ## License
183
+
184
+ MIT © Devarajan
@@ -0,0 +1,171 @@
1
+ # memoryos
2
+
3
+ [![CI](https://github.com/Devarajan8/memoryos/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/Devarajan8/memoryos/actions)
4
+
5
+ > Persistent, queryable memory for any Python AI app — local-first, zero API cost.
6
+
7
+ [![PyPI version](https://badge.fury.io/py/memoryos-official.svg)](https://pypi.org/project/memoryos-official/)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
10
+
11
+ ---
12
+
13
+ ## What is it?
14
+
15
+ `memoryos` gives any AI app a long-term memory layer — without needing an API key, a cloud service, or a database server.
16
+
17
+ Store things. Recall them semantically. Forget them. All locally.
18
+
19
+ ```python
20
+ from memoryos import Memory
21
+
22
+ mem = Memory(user_id="arjun")
23
+
24
+ mem.remember("I prefer dark mode and use VS Code")
25
+ mem.remember("Currently building a job application assistant")
26
+ mem.remember("I like concise answers over long explanations")
27
+
28
+ results = mem.recall("what tools does the user prefer?")
29
+ for r in results:
30
+ print(f"[{r['score']:.3f}] {r['text']}")
31
+ ```
32
+
33
+ ```
34
+ [0.412] I prefer dark mode and use VS Code
35
+ [0.289] I like concise answers over long explanations
36
+ ```
37
+
38
+ ## CLI Demo
39
+
40
+ ![alt text](image.png)
41
+
42
+ ### Stats
43
+
44
+ ![alt text](image-1.png)
45
+
46
+ ---
47
+
48
+ ## Install
49
+
50
+ ```bash
51
+ pip install memoryos-official
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Features
57
+
58
+ - **Semantic recall** — finds memories by meaning, not exact keywords
59
+ - **Memory decay** — old memories fade in relevance automatically
60
+ - **Importance scoring** — weight memories by how significant they are
61
+ - **Tags** — organize memories into namespaces
62
+ - **Forget & clear** — GDPR-friendly deletion
63
+ - **Local-first** — uses FAISS + SQLite, no external services
64
+ - **Zero cost** — no API keys, no cloud, runs on your machine
65
+
66
+ ---
67
+
68
+ ## API Reference
69
+
70
+ ### `Memory(user_id, db_path)`
71
+
72
+ ```python
73
+ mem = Memory(user_id="alice", db_path="memory.db")
74
+ ```
75
+
76
+ | Param | Default | Description |
77
+ | --------- | ------------- | ----------------------------------------------------- |
78
+ | `user_id` | `"default"` | Isolates memories per user |
79
+ | `db_path` | `"memory.db"` | SQLite file path. Use `":memory:"` for in-RAM (tests) |
80
+
81
+ ---
82
+
83
+ ### `remember(text, tags, importance, decay_days)`
84
+
85
+ Store a memory.
86
+
87
+ ```python
88
+ mid = mem.remember(
89
+ "User just got promoted to SDE-2",
90
+ tags=["career", "milestone"],
91
+ importance=0.9, # 0.0–1.0, default 0.5
92
+ decay_days=90, # score halves every 90 days, 0 = no decay
93
+ )
94
+ ```
95
+
96
+ Returns: `str` — the memory ID (use it to `forget()` later)
97
+
98
+ ---
99
+
100
+ ### `recall(query, top_k)`
101
+
102
+ Find the most semantically relevant memories.
103
+
104
+ ```python
105
+ results = mem.recall("what is the user's job status?", top_k=3)
106
+ # [{"id": ..., "text": ..., "score": 0.87, "tags": [...], ...}]
107
+ ```
108
+
109
+ ---
110
+
111
+ ### `forget(memory_id)`
112
+
113
+ Delete a specific memory.
114
+
115
+ ```python
116
+ mem.forget(mid)
117
+ ```
118
+
119
+ ---
120
+
121
+ ### `clear()`
122
+
123
+ Wipe all memories for this user.
124
+
125
+ ```python
126
+ mem.clear()
127
+ ```
128
+
129
+ ---
130
+
131
+ ### `export(filepath)`
132
+
133
+ Backup all memories to JSON.
134
+
135
+ ```python
136
+ mem.export("my_memories.json")
137
+ ```
138
+
139
+ ---
140
+
141
+ ## How it works
142
+
143
+ 1. **`remember()`** → text is embedded using `sentence-transformers` (all-MiniLM-L6-v2, runs locally) → vector stored in FAISS, metadata in SQLite
144
+ 2. **`recall()`** → query is embedded → FAISS finds top-k nearest vectors → SQLite returns the original text
145
+ 3. **Scoring** → final score = cosine similarity × (0.7 + 0.3 × decayed importance)
146
+
147
+ ---
148
+
149
+ ## Why not mem0 / Zep / etc.?
150
+
151
+ | | memoryos | mem0 | Zep |
152
+ | -------------- | -------- | ---------- | ---------- |
153
+ | Local-first | ✅ | ❌ | ❌ |
154
+ | API key needed | ❌ | ✅ | ✅ |
155
+ | Cost | Free | Paid tiers | Paid tiers |
156
+ | Memory decay | ✅ | ❌ | ❌ |
157
+ | `pip install` | ✅ | ✅ | ✅ |
158
+
159
+ ---
160
+
161
+ ## Stack
162
+
163
+ - [`sentence-transformers`](https://www.sbert.net/) — local text embeddings
164
+ - [`faiss-cpu`](https://github.com/facebookresearch/faiss) — vector similarity search
165
+ - `sqlite3` — metadata storage (built into Python)
166
+
167
+ ---
168
+
169
+ ## License
170
+
171
+ MIT © Devarajan
Binary file
Binary file
@@ -0,0 +1,28 @@
1
+ from memoryos import Memory
2
+
3
+ mem = Memory(user_id="arjun")
4
+ mem.remember("I prefer dark mode and use VS Code")
5
+ mem.remember("Currently building memoryos, a local memory library")
6
+ mem.remember("I like concise answers over long explanations")
7
+
8
+ results = mem.recall("what is the user building?")
9
+ for r in results:
10
+ print(f"[{r['score']:.3f}] {r['text']}")
11
+
12
+
13
+ from memoryos import Memory
14
+
15
+ mem = Memory(user_id="arjun", db_path=":memory:")
16
+ mid = mem.remember("Old info", decay_days=1)
17
+ mem.remember("User prefers Python", importance=0.9)
18
+ mem.remember("User said hi", importance=0.1)
19
+ mem.remember("Wants dark UI", tags=["ui", "preference"])
20
+
21
+ results = mem.recall("what does the user prefer?", top_k=3)
22
+ for r in results:
23
+ print(f"[{r['score']:.3f}] {r['text']}")
24
+
25
+ mem.forget(mid)
26
+ print("After forget:", len(mem.recall("old info")), "results")
27
+ mem.clear()
28
+ print("After clear:", len(mem.recall("anything")), "results")
@@ -0,0 +1,109 @@
1
+
2
+ import os
3
+ from memoryos import Memory
4
+
5
+ from pathlib import Path
6
+
7
+ _env_file = Path(__file__).parent.parent / ".env"
8
+ if _env_file.exists():
9
+ for line in _env_file.read_text().splitlines():
10
+ line = line.strip()
11
+ if line and not line.startswith("#") and "=" in line:
12
+ key, _, value = line.partition("=")
13
+ os.environ.setdefault(key.strip(), value.strip().strip('"').strip("'"))
14
+
15
+
16
+ try:
17
+ from groq import Groq
18
+ HAS_GROQ = True
19
+ except ImportError:
20
+ HAS_GROQ = False
21
+
22
+ def get_memory_context(mem: Memory, user_message: str) -> str:
23
+
24
+ results = mem.recall(user_message, top_k=1)
25
+ if not results:
26
+ return ""
27
+ context = "\n".join(f"- {r['text']}" for r in results)
28
+ return f"\nRelevant things I remember about you:\n{context}\n"
29
+
30
+
31
+ def chat_with_memory():
32
+ mem = Memory(user_id="demo_user", db_path="chatbot_memory.db")
33
+ print("Chatbot with memory (type 'quit' to exit, 'forget all' to clear)")
34
+ print("\n")
35
+
36
+ if HAS_GROQ:
37
+ client = Groq(api_key=os.environ.get("GROQ_API_KEY"))
38
+ else:
39
+ print(" Groq not installed — running in echo mode (no LLM)")
40
+ print(" Install: pip install groq")
41
+ print(" Get free API key: console.groq.com")
42
+ print()
43
+
44
+ conversation_history = []
45
+
46
+ while True:
47
+ user_input = input("You: ").strip()
48
+ if not user_input:
49
+ continue
50
+ if user_input.lower() == "quit":
51
+ break
52
+ if user_input.lower() == "forget all":
53
+ mem.clear()
54
+ print("Bot: Memory cleared.\n")
55
+ continue
56
+
57
+ def should_remember(user_input: str, client) -> bool:
58
+
59
+ response = client.chat.completions.create(
60
+ model="llama-3.3-70b-versatile",
61
+ messages=[{
62
+ "role": "user",
63
+ "content": (
64
+ f"Does this message contain a personal fact, preference, or "
65
+ f"long-term information worth remembering about the user?\n\n"
66
+ f"Message: \"{user_input}\"\n\n"
67
+ f"Reply with only YES or NO."
68
+ )
69
+ }],
70
+ max_tokens=5,
71
+ temperature=0,
72
+ )
73
+ answer = response.choices[0].message.content.strip().upper()
74
+ return answer.startswith("YES")
75
+
76
+
77
+ if HAS_GROQ and should_remember(user_input, client):
78
+ mem.remember(user_input, importance=0.8)
79
+ print(" Stored in memory")
80
+
81
+
82
+ memory_context = get_memory_context(mem, user_input)
83
+
84
+ if HAS_GROQ:
85
+ system_prompt = (
86
+ "You are a helpful assistant with memory. "
87
+ "Use the remembered context to personalise your responses."
88
+ + memory_context
89
+ )
90
+ conversation_history.append({"role": "user", "content": user_input})
91
+ response = client.chat.completions.create(
92
+ model="llama-3.3-70b-versatile",
93
+ messages=[{"role": "system", "content": system_prompt}]
94
+ + conversation_history[-6:],
95
+ )
96
+ reply = response.choices[0].message.content
97
+ conversation_history.append({"role": "assistant", "content": reply})
98
+ print(f"Bot: {reply}\n")
99
+ else:
100
+ # Demo mode without LLM
101
+ if memory_context:
102
+ print(f"Bot: [Echo + Memory] {user_input}")
103
+ print(f" Context I have:{memory_context}")
104
+ else:
105
+ print(f"Bot: [Echo] {user_input}\n")
106
+
107
+
108
+ if __name__ == "__main__":
109
+ chat_with_memory()
retainr-0.2.0/hello.py ADDED
@@ -0,0 +1,6 @@
1
+ def main():
2
+ print("Hello from memoryos!")
3
+
4
+
5
+ if __name__ == "__main__":
6
+ main()
Binary file
Binary file
Binary file
@@ -0,0 +1,4 @@
1
+ from .memory import Memory
2
+
3
+ __all__ = ["Memory"]
4
+ __version__ = "0.1.0"
@@ -0,0 +1,65 @@
1
+
2
+ import sys
3
+ from memoryos import Memory, __version__
4
+
5
+ import os
6
+ from pathlib import Path
7
+
8
+
9
+ _env_file = Path(__file__).parent.parent / ".env"
10
+ if _env_file.exists():
11
+ for line in _env_file.read_text().splitlines():
12
+ line = line.strip()
13
+ if line and not line.startswith("#") and "=" in line:
14
+ key, _, value = line.partition("=")
15
+ os.environ.setdefault(key.strip(), value.strip().strip('"').strip("'"))
16
+
17
+
18
+ def main():
19
+ print(f"memoryos v{__version__} — local AI memory")
20
+ print("Commands: remember <text> | recall <query> | clear | stats")
21
+ print()
22
+
23
+ mem = Memory(user_id="cli_user", db_path="cli_memory.db")
24
+
25
+ if len(sys.argv) < 2:
26
+ print("Usage: python -m memoryos remember 'some text'")
27
+ print(" python -m memoryos recall 'your query'")
28
+ print(" python -m memoryos clear")
29
+ print(" python -m memoryos stats")
30
+ return
31
+
32
+ command = sys.argv[1].lower()
33
+ args = " ".join(sys.argv[2:])
34
+
35
+ if command == "remember":
36
+ if not args:
37
+ print("Error: provide text to remember")
38
+ return
39
+ mid = mem.remember(args)
40
+ print(f"Remembered (id: {mid[:8]}...)")
41
+
42
+ elif command == "recall":
43
+ if not args:
44
+ print("Error: provide a query")
45
+ return
46
+ results = mem.recall(args, top_k=1)
47
+ if not results:
48
+ print("No memories found.")
49
+ for r in results:
50
+ print(f"[{r['score']:.3f}] {r['text']}")
51
+
52
+ elif command == "clear":
53
+ mem.clear()
54
+ print(" All memories cleared.")
55
+
56
+ elif command == "stats":
57
+ results = mem.recall("", top_k=99999)
58
+ print(f"Total memories: {len(results)}")
59
+
60
+ else:
61
+ print(f"Unknown command: {command}")
62
+
63
+
64
+ if __name__ == "__main__":
65
+ main()
@@ -0,0 +1,11 @@
1
+ from sentence_transformers import SentenceTransformer
2
+ import numpy as np
3
+
4
+ class Embedder:
5
+ def __init__(self, model_name: str = "all-MiniLM-L6-v2"):
6
+
7
+ self.model = SentenceTransformer(model_name)
8
+
9
+ def encode(self, text: str) -> np.ndarray:
10
+
11
+ return self.model.encode(text, normalize_embeddings=True)
@@ -0,0 +1,38 @@
1
+ import time
2
+ from .embedder import Embedder
3
+ from .store import VectorStore
4
+
5
+ class Memory:
6
+ def __init__(self, user_id: str = "default", db_path: str = "memory.db"):
7
+ self.user_id = user_id
8
+ self.embedder = Embedder()
9
+ self.store = VectorStore(db_path)
10
+
11
+ def forget(self, memory_id: str) -> bool:
12
+ return self.store.delete(memory_id)
13
+
14
+ def clear(self):
15
+ self.store.clear(self.user_id)
16
+
17
+ def export(self, filepath: str):
18
+ import json
19
+ # Get all memories (high top_k to get everything)
20
+ results = self.store.search(self.user_id,
21
+ self.embedder.encode(""), top_k=99999)
22
+ with open(filepath, "w") as f:
23
+ json.dump(results, f, indent=2)
24
+
25
+ def remember(self, text: str, tags: list[str] = [],
26
+ importance: float = 0.5, decay_days: int = 0) -> str:
27
+
28
+ embedding = self.embedder.encode(text)
29
+ return self.store.save(
30
+ user_id=self.user_id, text=text, embedding=embedding,
31
+ tags=tags, timestamp=time.time(),
32
+ importance=importance, decay_days=decay_days,
33
+ )
34
+
35
+ def recall(self, query: str, top_k: int = 1) -> list[dict]:
36
+
37
+ query_embedding = self.embedder.encode(query)
38
+ return self.store.search(self.user_id, query_embedding, top_k)