AthelaPerk commited on
Commit
aa2e15a
Β·
verified Β·
1 Parent(s): c8d97f7

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +246 -61
app.py CHANGED
@@ -1,76 +1,261 @@
 
 
 
 
 
1
  import gradio as gr
2
- import hashlib
3
  import time
4
- import numpy as np
5
-
6
- memories = {}
7
- stats = {"searches": 0, "adds": 0}
8
 
9
- def get_embedding(text):
10
- emb = np.zeros(128, dtype=np.float32)
11
- for i, word in enumerate(text.lower().split()):
12
- emb[hash(word) % 128] += 1.0 / (i + 1)
13
- norm = np.linalg.norm(emb)
14
- return emb / norm if norm > 0 else emb
15
 
16
- def add_memory(content):
17
- if not content or not content.strip():
18
- return "Please enter content"
19
- mid = f"mem_{hashlib.md5(content.encode()).hexdigest()[:8]}"
20
- memories[mid] = {"content": content.strip(), "embedding": get_embedding(content)}
21
- stats["adds"] += 1
22
- return f"Added: {mid} (total: {len(memories)})"
 
 
 
 
 
 
 
 
 
 
 
23
 
24
- def search_memories(query):
25
- if not query or not query.strip():
26
- return "Please enter a query"
27
- if not memories:
28
- return "No memories yet"
29
 
 
30
  start = time.time()
31
- query_emb = get_embedding(query)
32
- results = []
33
- for mid, mem in memories.items():
34
- score = float(np.dot(query_emb, mem["embedding"]))
35
- results.append((mid, mem["content"], score))
36
- results.sort(key=lambda x: x[2], reverse=True)
 
 
37
  latency = (time.time() - start) * 1000
38
- stats["searches"] += 1
39
 
40
- output = f"Found {len(results[:5])} results in {latency:.2f}ms\n\n"
41
- for i, (mid, content, score) in enumerate(results[:5], 1):
42
- output += f"{i}. [{mid}] {score:.3f}\n {content}\n\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  return output
44
 
45
- def get_stats():
46
- return f"Memories: {len(memories)} | Searches: {stats['searches']} | Adds: {stats['adds']}"
 
 
 
 
 
 
 
 
47
 
48
- def reset_all():
49
- memories.clear()
50
- for ex in ["User prefers dark mode", "Project deadline March 15", "Favorite coffee cappuccino"]:
51
- add_memory(ex)
52
- return f"Reset. {len(memories)} memories."
53
 
54
- reset_all()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
- with gr.Blocks(title="Mnemo") as demo:
57
- gr.Markdown("# 🧠 Mnemo\n**Memory for LLMs - 21x faster than mem0**")
58
-
59
- with gr.Tab("Search"):
60
- q = gr.Textbox(label="Query", placeholder="e.g., preferences")
61
- search_btn = gr.Button("Search", variant="primary")
62
- out = gr.Textbox(label="Results", lines=8)
63
- search_btn.click(search_memories, q, out)
64
-
65
- with gr.Tab("Add"):
66
- c = gr.Textbox(label="Content")
67
- add_btn = gr.Button("Add")
68
- add_out = gr.Textbox(label="Status")
69
- add_btn.click(add_memory, c, add_out)
70
-
71
- with gr.Tab("Stats"):
72
- s = gr.Textbox(value=get_stats(), label="Stats")
73
- gr.Button("Refresh").click(get_stats, outputs=s)
74
- gr.Button("Reset").click(reset_all, outputs=s)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
 
76
- demo.launch()
 
 
1
+ """
2
+ Mnemo v2 - Interactive Demo
3
+ Enhanced memory system with real embeddings, HNSW index, and temporal decay.
4
+ """
5
+
6
  import gradio as gr
 
7
  import time
8
+ from datetime import datetime
9
+ from mnemo_core import get_mnemo
 
 
10
 
11
+ def format_time(timestamp: float) -> str:
12
+ return datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
 
 
 
 
13
 
14
+ def add_memory(content: str, importance: float, tags: str, user_id: str):
15
+ if not content.strip():
16
+ return "❌ Please enter content", get_stats_text(user_id)
17
+
18
+ mnemo = get_mnemo()
19
+ tags_list = [t.strip() for t in tags.split(",") if t.strip()] if tags else []
20
+
21
+ result = mnemo.add(
22
+ content=content,
23
+ user_id=user_id or "default",
24
+ importance=importance,
25
+ tags=tags_list
26
+ )
27
+
28
+ op = result['operation']
29
+ icon = {"ADD": "βœ…", "UPDATE": "πŸ”„", "NOOP": "⚠️"}.get(op, "❓")
30
+
31
+ return f"{icon} {result['message']} (ID: {result['id']})", get_stats_text(user_id)
32
 
33
+ def search_memories(query: str, k: int, min_score: float, user_id: str):
34
+ if not query.strip():
35
+ return "❌ Please enter a search query"
 
 
36
 
37
+ mnemo = get_mnemo()
38
  start = time.time()
39
+
40
+ results = mnemo.search(
41
+ query=query,
42
+ user_id=user_id or "default",
43
+ k=k,
44
+ min_score=min_score
45
+ )
46
+
47
  latency = (time.time() - start) * 1000
 
48
 
49
+ if not results:
50
+ return f"No results found (searched in {latency:.1f}ms)"
51
+
52
+ output = f"**Found {len(results)} results in {latency:.1f}ms**\n\n"
53
+
54
+ for i, r in enumerate(results, 1):
55
+ output += f"### {i}. [{r['id']}]\n"
56
+ output += f"**Content:** {r['content']}\n\n"
57
+ output += f"- Relevance: `{r['relevance_score']:.3f}`\n"
58
+ output += f"- Similarity: `{r['similarity']:.3f}`\n"
59
+ output += f"- Decay: `{r['decay_score']:.3f}`\n"
60
+ output += f"- Importance: `{r['importance']:.2f}`\n"
61
+ output += f"- Accesses: `{r['access_count']}`\n"
62
+ output += f"- Last accessed: `{format_time(r['last_accessed'])}`\n"
63
+ if r['tags']:
64
+ output += f"- Tags: `{', '.join(r['tags'])}`\n"
65
+ output += "\n---\n\n"
66
+
67
+ return output
68
+
69
+ def list_memories_ui(user_id: str, limit: int):
70
+ mnemo = get_mnemo()
71
+ memories = mnemo.list_memories(user_id=user_id or "default", limit=limit)
72
+
73
+ if not memories:
74
+ return "No memories stored yet."
75
+
76
+ output = f"**{len(memories)} memories (sorted by last accessed)**\n\n"
77
+
78
+ for m in memories:
79
+ output += f"**{m['id']}** (importance: {m['importance']:.2f}, accesses: {m['access_count']})\n"
80
+ output += f"> {m['content'][:100]}{'...' if len(m['content']) > 100 else ''}\n"
81
+ output += f"- Created: {format_time(m['created_at'])} | Last: {format_time(m['last_accessed'])}\n"
82
+ if m['tags']:
83
+ output += f"- Tags: {', '.join(m['tags'])}\n"
84
+ output += "\n"
85
+
86
  return output
87
 
88
+ def delete_memory_ui(memory_id: str, user_id: str):
89
+ if not memory_id.strip():
90
+ return "❌ Please enter a memory ID", get_stats_text(user_id)
91
+
92
+ mnemo = get_mnemo()
93
+ success = mnemo.delete(memory_id.strip(), user_id=user_id or "default")
94
+
95
+ if success:
96
+ return f"βœ… Deleted memory: {memory_id}", get_stats_text(user_id)
97
+ return f"❌ Memory not found: {memory_id}", get_stats_text(user_id)
98
 
99
+ def clear_memories_ui(user_id: str):
100
+ mnemo = get_mnemo()
101
+ count = mnemo.clear(user_id=user_id or "default")
102
+ return f"πŸ—‘οΈ Cleared {count} memories", get_stats_text(user_id)
 
103
 
104
+ def get_stats_text(user_id: str = "default") -> str:
105
+ mnemo = get_mnemo()
106
+ stats = mnemo.get_stats(user_id=user_id or "default")
107
+
108
+ return f"""**System Stats**
109
+ - Total memories: {stats['total_memories']}
110
+ - User memories: {stats['user_memory_count']}
111
+ - Total users: {stats['total_users']}
112
+ - Adds: {stats['total_adds']} | Updates: {stats['total_updates']} | Deletes: {stats['total_deletes']}
113
+ - Searches: {stats['total_searches']}
114
+ - Decay half-life: {stats['decay_half_life_days']:.1f} days"""
115
+
116
+ def load_examples(user_id: str):
117
+ mnemo = get_mnemo()
118
+
119
+ examples = [
120
+ ("User prefers dark mode and VS Code for development", 1.0, ["preferences", "development"]),
121
+ ("Project deadline is March 15th 2026 for the Q1 release", 0.9, ["project", "deadline"]),
122
+ ("Favorite programming language is Python, also uses TypeScript", 0.8, ["preferences", "languages"]),
123
+ ("Weekly standup meetings are every Monday at 10am", 0.7, ["meetings", "schedule"]),
124
+ ("User is allergic to peanuts - important health info", 1.0, ["health", "critical"]),
125
+ ("Prefers cappuccino with oat milk for coffee orders", 0.5, ["preferences", "food"]),
126
+ ("Working on a machine learning project for recommendation systems", 0.8, ["project", "ml"]),
127
+ ("Lives in San Francisco, timezone is PST", 0.6, ["personal", "location"]),
128
+ ]
129
+
130
+ added = 0
131
+ for content, importance, tags in examples:
132
+ result = mnemo.add(
133
+ content=content,
134
+ user_id=user_id or "default",
135
+ importance=importance,
136
+ tags=tags
137
+ )
138
+ if result['operation'] in ('ADD', 'UPDATE'):
139
+ added += 1
140
+
141
+ return f"βœ… Loaded {added} example memories", get_stats_text(user_id)
142
 
143
+ # Build the Gradio interface
144
+ with gr.Blocks(title="Mnemo v2", theme=gr.themes.Soft()) as demo:
145
+ gr.Markdown("""
146
+ # 🧠 Mnemo v2 - Enhanced Memory for LLMs
147
+
148
+ **Features:**
149
+ - πŸ” Real semantic embeddings (sentence-transformers)
150
+ - ⚑ HNSW index for O(log n) search (faiss)
151
+ - πŸ“‰ Temporal decay (Ebbinghaus forgetting curve)
152
+ - πŸ“Š Composite relevance scoring (similarity + recency + frequency)
153
+ - πŸ”„ Intelligent ADD/UPDATE detection
154
+ - πŸ‘₯ Multi-user support
155
+ """)
156
+
157
+ with gr.Row():
158
+ user_id_input = gr.Textbox(
159
+ label="User ID",
160
+ value="default",
161
+ placeholder="Enter user ID for multi-user support",
162
+ scale=2
163
+ )
164
+ load_btn = gr.Button("πŸ“₯ Load Examples", scale=1)
165
+
166
+ stats_display = gr.Markdown(get_stats_text())
167
+
168
+ with gr.Tabs():
169
+ with gr.Tab("πŸ” Search"):
170
+ search_query = gr.Textbox(
171
+ label="Search Query",
172
+ placeholder="e.g., What are the user's preferences?"
173
+ )
174
+ with gr.Row():
175
+ search_k = gr.Slider(1, 20, value=5, step=1, label="Max Results")
176
+ search_min_score = gr.Slider(0, 1, value=0, step=0.05, label="Min Score")
177
+ search_btn = gr.Button("Search", variant="primary")
178
+ search_output = gr.Markdown()
179
+
180
+ search_btn.click(
181
+ search_memories,
182
+ inputs=[search_query, search_k, search_min_score, user_id_input],
183
+ outputs=search_output
184
+ )
185
+
186
+ with gr.Tab("βž• Add Memory"):
187
+ add_content = gr.Textbox(
188
+ label="Memory Content",
189
+ placeholder="e.g., User prefers dark mode",
190
+ lines=3
191
+ )
192
+ with gr.Row():
193
+ add_importance = gr.Slider(0, 1, value=1.0, step=0.1, label="Importance")
194
+ add_tags = gr.Textbox(
195
+ label="Tags (comma-separated)",
196
+ placeholder="e.g., preferences, settings"
197
+ )
198
+ add_btn = gr.Button("Add Memory", variant="primary")
199
+ add_output = gr.Textbox(label="Result")
200
+
201
+ add_btn.click(
202
+ add_memory,
203
+ inputs=[add_content, add_importance, add_tags, user_id_input],
204
+ outputs=[add_output, stats_display]
205
+ )
206
+
207
+ with gr.Tab("πŸ“‹ List Memories"):
208
+ list_limit = gr.Slider(10, 100, value=50, step=10, label="Limit")
209
+ list_btn = gr.Button("List All Memories")
210
+ list_output = gr.Markdown()
211
+
212
+ list_btn.click(
213
+ list_memories_ui,
214
+ inputs=[user_id_input, list_limit],
215
+ outputs=list_output
216
+ )
217
+
218
+ with gr.Tab("βš™οΈ Manage"):
219
+ gr.Markdown("### Delete Memory")
220
+ delete_id = gr.Textbox(label="Memory ID", placeholder="e.g., mem_abc123")
221
+ delete_btn = gr.Button("Delete", variant="stop")
222
+ delete_output = gr.Textbox(label="Result")
223
+
224
+ delete_btn.click(
225
+ delete_memory_ui,
226
+ inputs=[delete_id, user_id_input],
227
+ outputs=[delete_output, stats_display]
228
+ )
229
+
230
+ gr.Markdown("### Clear All Memories")
231
+ clear_btn = gr.Button("πŸ—‘οΈ Clear All (for current user)", variant="stop")
232
+ clear_output = gr.Textbox(label="Result")
233
+
234
+ clear_btn.click(
235
+ clear_memories_ui,
236
+ inputs=[user_id_input],
237
+ outputs=[clear_output, stats_display]
238
+ )
239
+
240
+ load_btn.click(
241
+ load_examples,
242
+ inputs=[user_id_input],
243
+ outputs=[stats_display, stats_display]
244
+ )
245
+
246
+ gr.Markdown("""
247
+ ---
248
+ ### Architecture
249
+
250
+ | Component | Implementation |
251
+ |-----------|----------------|
252
+ | Embeddings | `sentence-transformers/all-MiniLM-L6-v2` (384 dim) |
253
+ | Vector Index | `faiss-cpu` HNSW (M=32, efSearch=50) |
254
+ | Decay | Ebbinghaus curve, 7-day half-life |
255
+ | Scoring | 40% similarity + 30% recency + 30% frequency |
256
+
257
+ **Links:** [GitHub](https://huggingface.co/AthelaPerk/mnemo-memory) | [API](https://huggingface.co/spaces/AthelaPerk/mnemo-mcp)
258
+ """)
259
 
260
+ if __name__ == "__main__":
261
+ demo.launch()