Update app.py with smart injection and real embeddings
Browse files
app.py
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Mnemo Demo - Gradio Interface
|
| 3 |
+
Demonstrates smart memory injection and semantic search
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import gradio as gr
|
| 7 |
+
from mnemo import Mnemo, should_inject_memory
|
| 8 |
+
|
| 9 |
+
# Initialize global Mnemo instance
|
| 10 |
+
memory = Mnemo(use_real_embeddings=True)
|
| 11 |
+
|
| 12 |
+
# Pre-load some example memories
|
| 13 |
+
EXAMPLE_MEMORIES = [
|
| 14 |
+
"User prefers Python programming and dark mode interfaces",
|
| 15 |
+
"Project deadline is March 15th for the API redesign",
|
| 16 |
+
"Previous analysis identified gender bias in Victorian psychiatry diagnoses",
|
| 17 |
+
"Framework has 5 checkpoints: gender, class, age, occupation, marital status",
|
| 18 |
+
"User's favorite coffee is cappuccino with oat milk, no sugar",
|
| 19 |
+
"Team standup meeting every Tuesday at 2pm in conference room 401",
|
| 20 |
+
"Working on machine learning model for customer churn prediction"
|
| 21 |
+
]
|
| 22 |
+
|
| 23 |
+
def initialize_demo():
|
| 24 |
+
"""Initialize with example memories"""
|
| 25 |
+
memory.clear()
|
| 26 |
+
for mem in EXAMPLE_MEMORIES:
|
| 27 |
+
memory.add(mem)
|
| 28 |
+
return f"β
Initialized with {len(EXAMPLE_MEMORIES)} example memories"
|
| 29 |
+
|
| 30 |
+
def add_memory(content: str):
|
| 31 |
+
"""Add a new memory"""
|
| 32 |
+
if not content.strip():
|
| 33 |
+
return "β Please enter some content", get_all_memories()
|
| 34 |
+
|
| 35 |
+
mem_id = memory.add(content.strip())
|
| 36 |
+
return f"β
Added memory: {mem_id}", get_all_memories()
|
| 37 |
+
|
| 38 |
+
def search_memories(query: str, top_k: int = 5):
|
| 39 |
+
"""Search memories"""
|
| 40 |
+
if not query.strip():
|
| 41 |
+
return "β Please enter a search query"
|
| 42 |
+
|
| 43 |
+
results = memory.search(query.strip(), top_k=int(top_k))
|
| 44 |
+
|
| 45 |
+
if not results:
|
| 46 |
+
return "No memories found"
|
| 47 |
+
|
| 48 |
+
output = []
|
| 49 |
+
for r in results:
|
| 50 |
+
output.append(f"**[{r.id}]** (score: {r.score:.3f})")
|
| 51 |
+
output.append(f"{r.content}")
|
| 52 |
+
output.append(f"_Strategies: semantic={r.strategy_scores.get('semantic', 0):.2f}, bm25={r.strategy_scores.get('bm25', 0):.2f}, graph={r.strategy_scores.get('graph', 0):.2f}_")
|
| 53 |
+
output.append("---")
|
| 54 |
+
|
| 55 |
+
return "\n".join(output)
|
| 56 |
+
|
| 57 |
+
def check_injection(query: str, context: str = ""):
|
| 58 |
+
"""Check if memory should be injected"""
|
| 59 |
+
should, reason = should_inject_memory(query, context)
|
| 60 |
+
|
| 61 |
+
status = "β
**INJECT MEMORY**" if should else "βοΈ **SKIP MEMORY**"
|
| 62 |
+
|
| 63 |
+
return f"{status}\n\nReason: `{reason}`"
|
| 64 |
+
|
| 65 |
+
def get_context_for_injection(query: str, top_k: int = 3):
|
| 66 |
+
"""Get formatted context for prompt injection"""
|
| 67 |
+
if not query.strip():
|
| 68 |
+
return "β Please enter a query"
|
| 69 |
+
|
| 70 |
+
context = memory.get_context(query.strip(), top_k=int(top_k))
|
| 71 |
+
|
| 72 |
+
if not context:
|
| 73 |
+
return "_No relevant context found_"
|
| 74 |
+
|
| 75 |
+
return f"```\n{context}\n```"
|
| 76 |
+
|
| 77 |
+
def get_all_memories():
|
| 78 |
+
"""Get all stored memories"""
|
| 79 |
+
if len(memory) == 0:
|
| 80 |
+
return "_No memories stored_"
|
| 81 |
+
|
| 82 |
+
output = []
|
| 83 |
+
for mem in memory.list_all():
|
| 84 |
+
output.append(f"β’ **{mem.id}**: {mem.content[:100]}...")
|
| 85 |
+
|
| 86 |
+
return "\n".join(output)
|
| 87 |
+
|
| 88 |
+
def get_stats():
|
| 89 |
+
"""Get system statistics"""
|
| 90 |
+
stats = memory.get_stats()
|
| 91 |
+
|
| 92 |
+
output = []
|
| 93 |
+
for k, v in stats.items():
|
| 94 |
+
output.append(f"β’ **{k}**: {v}")
|
| 95 |
+
|
| 96 |
+
return "\n".join(output)
|
| 97 |
+
|
| 98 |
+
def clear_memories():
|
| 99 |
+
"""Clear all memories"""
|
| 100 |
+
memory.clear()
|
| 101 |
+
return "β
All memories cleared", "_No memories stored_"
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
# Gradio Interface
|
| 105 |
+
with gr.Blocks(title="Mnemo - AI Memory System", theme=gr.themes.Soft()) as demo:
|
| 106 |
+
gr.Markdown("""
|
| 107 |
+
# π§ Mnemo - AI Memory System
|
| 108 |
+
|
| 109 |
+
**Smart memory injection for LLMs, chatbots, and AI agents**
|
| 110 |
+
|
| 111 |
+
> 21x faster than mem0 β’ Context-check algorithm β’ Real embeddings
|
| 112 |
+
""")
|
| 113 |
+
|
| 114 |
+
with gr.Tab("πΎ Memory Store"):
|
| 115 |
+
gr.Markdown("### Add and view memories")
|
| 116 |
+
|
| 117 |
+
with gr.Row():
|
| 118 |
+
with gr.Column(scale=2):
|
| 119 |
+
memory_input = gr.Textbox(
|
| 120 |
+
label="New Memory",
|
| 121 |
+
placeholder="Enter a memory to store...",
|
| 122 |
+
lines=2
|
| 123 |
+
)
|
| 124 |
+
add_btn = gr.Button("β Add Memory", variant="primary")
|
| 125 |
+
add_status = gr.Markdown()
|
| 126 |
+
|
| 127 |
+
with gr.Column(scale=3):
|
| 128 |
+
memories_display = gr.Markdown(label="Stored Memories")
|
| 129 |
+
|
| 130 |
+
with gr.Row():
|
| 131 |
+
init_btn = gr.Button("π Load Example Memories")
|
| 132 |
+
clear_btn = gr.Button("ποΈ Clear All")
|
| 133 |
+
|
| 134 |
+
add_btn.click(add_memory, inputs=[memory_input], outputs=[add_status, memories_display])
|
| 135 |
+
init_btn.click(initialize_demo, outputs=[add_status])
|
| 136 |
+
init_btn.click(get_all_memories, outputs=[memories_display])
|
| 137 |
+
clear_btn.click(clear_memories, outputs=[add_status, memories_display])
|
| 138 |
+
|
| 139 |
+
with gr.Tab("π Search"):
|
| 140 |
+
gr.Markdown("### Search stored memories")
|
| 141 |
+
|
| 142 |
+
with gr.Row():
|
| 143 |
+
search_input = gr.Textbox(
|
| 144 |
+
label="Search Query",
|
| 145 |
+
placeholder="Enter search query...",
|
| 146 |
+
lines=1
|
| 147 |
+
)
|
| 148 |
+
top_k_slider = gr.Slider(
|
| 149 |
+
minimum=1, maximum=10, value=5, step=1,
|
| 150 |
+
label="Number of Results"
|
| 151 |
+
)
|
| 152 |
+
|
| 153 |
+
search_btn = gr.Button("π Search", variant="primary")
|
| 154 |
+
search_results = gr.Markdown(label="Results")
|
| 155 |
+
|
| 156 |
+
search_btn.click(
|
| 157 |
+
search_memories,
|
| 158 |
+
inputs=[search_input, top_k_slider],
|
| 159 |
+
outputs=[search_results]
|
| 160 |
+
)
|
| 161 |
+
|
| 162 |
+
with gr.Tab("π― Smart Injection"):
|
| 163 |
+
gr.Markdown("""
|
| 164 |
+
### Should memory be injected?
|
| 165 |
+
|
| 166 |
+
The context-check algorithm detects if a query references prior context and needs memory injection.
|
| 167 |
+
|
| 168 |
+
**Inject when:** "Based on your previous analysis...", "Compare to earlier...", "Using your framework..."
|
| 169 |
+
|
| 170 |
+
**Skip when:** "What is Python?", "This is a NEW topic...", standalone questions
|
| 171 |
+
""")
|
| 172 |
+
|
| 173 |
+
with gr.Row():
|
| 174 |
+
with gr.Column():
|
| 175 |
+
injection_query = gr.Textbox(
|
| 176 |
+
label="Query to Check",
|
| 177 |
+
placeholder="Enter a query to check...",
|
| 178 |
+
lines=2
|
| 179 |
+
)
|
| 180 |
+
injection_context = gr.Textbox(
|
| 181 |
+
label="Additional Context (optional)",
|
| 182 |
+
placeholder="Any additional context...",
|
| 183 |
+
lines=1
|
| 184 |
+
)
|
| 185 |
+
check_btn = gr.Button("π― Check Injection", variant="primary")
|
| 186 |
+
|
| 187 |
+
with gr.Column():
|
| 188 |
+
injection_result = gr.Markdown(label="Result")
|
| 189 |
+
|
| 190 |
+
check_btn.click(
|
| 191 |
+
check_injection,
|
| 192 |
+
inputs=[injection_query, injection_context],
|
| 193 |
+
outputs=[injection_result]
|
| 194 |
+
)
|
| 195 |
+
|
| 196 |
+
gr.Markdown("### Get Context for Injection")
|
| 197 |
+
|
| 198 |
+
with gr.Row():
|
| 199 |
+
context_query = gr.Textbox(
|
| 200 |
+
label="Query",
|
| 201 |
+
placeholder="Enter query to get context for...",
|
| 202 |
+
lines=1
|
| 203 |
+
)
|
| 204 |
+
context_top_k = gr.Slider(
|
| 205 |
+
minimum=1, maximum=5, value=3, step=1,
|
| 206 |
+
label="Number of Memories"
|
| 207 |
+
)
|
| 208 |
+
|
| 209 |
+
context_btn = gr.Button("π Get Context")
|
| 210 |
+
context_output = gr.Markdown(label="Formatted Context")
|
| 211 |
+
|
| 212 |
+
context_btn.click(
|
| 213 |
+
get_context_for_injection,
|
| 214 |
+
inputs=[context_query, context_top_k],
|
| 215 |
+
outputs=[context_output]
|
| 216 |
+
)
|
| 217 |
+
|
| 218 |
+
with gr.Tab("π Stats"):
|
| 219 |
+
gr.Markdown("### System Statistics")
|
| 220 |
+
|
| 221 |
+
stats_btn = gr.Button("π Refresh Stats")
|
| 222 |
+
stats_display = gr.Markdown()
|
| 223 |
+
|
| 224 |
+
stats_btn.click(get_stats, outputs=[stats_display])
|
| 225 |
+
|
| 226 |
+
with gr.Tab("βΉοΈ About"):
|
| 227 |
+
gr.Markdown("""
|
| 228 |
+
## About Mnemo
|
| 229 |
+
|
| 230 |
+
Mnemo (named after Mnemosyne, Greek goddess of memory) is an open-source memory system for AI applications.
|
| 231 |
+
|
| 232 |
+
### Features
|
| 233 |
+
|
| 234 |
+
- **π― Smart Injection** - Context-check algorithm with 90% accuracy
|
| 235 |
+
- **𧬠Real Embeddings** - sentence-transformers (with hash fallback)
|
| 236 |
+
- **π Multi-Strategy Search** - Semantic + BM25 + Knowledge Graph
|
| 237 |
+
- **π Feedback Learning** - Improves over time
|
| 238 |
+
- **β‘ Fast** - 21x faster than mem0
|
| 239 |
+
- **π Private** - Fully local, no API keys needed
|
| 240 |
+
|
| 241 |
+
### Installation
|
| 242 |
+
|
| 243 |
+
```bash
|
| 244 |
+
pip install mnemo-memory
|
| 245 |
+
```
|
| 246 |
+
|
| 247 |
+
### Quick Start
|
| 248 |
+
|
| 249 |
+
```python
|
| 250 |
+
from mnemo import Mnemo
|
| 251 |
+
|
| 252 |
+
m = Mnemo()
|
| 253 |
+
m.add("User prefers dark mode")
|
| 254 |
+
|
| 255 |
+
if m.should_inject("Based on user preferences..."):
|
| 256 |
+
context = m.get_context("preferences")
|
| 257 |
+
# Use context in prompt
|
| 258 |
+
```
|
| 259 |
+
|
| 260 |
+
### Links
|
| 261 |
+
|
| 262 |
+
- [HuggingFace Model](https://huggingface.co/AthelaPerk/mnemo-memory)
|
| 263 |
+
- [MCP Server](https://huggingface.co/spaces/AthelaPerk/mnemo-mcp)
|
| 264 |
+
|
| 265 |
+
---
|
| 266 |
+
|
| 267 |
+
MIT License β’ Built with β€οΈ for the AI community
|
| 268 |
+
""")
|
| 269 |
+
|
| 270 |
+
|
| 271 |
+
# Initialize with examples on load
|
| 272 |
+
demo.load(initialize_demo)
|
| 273 |
+
demo.load(get_all_memories, outputs=[])
|
| 274 |
+
|
| 275 |
+
if __name__ == "__main__":
|
| 276 |
+
demo.launch()
|