yourmemory 1.2.0__tar.gz → 1.2.2__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.
Files changed (35) hide show
  1. {yourmemory-1.2.0/yourmemory.egg-info → yourmemory-1.2.2}/PKG-INFO +70 -57
  2. {yourmemory-1.2.0 → yourmemory-1.2.2}/README.md +66 -53
  3. {yourmemory-1.2.0 → yourmemory-1.2.2}/memory_mcp.py +34 -0
  4. {yourmemory-1.2.0 → yourmemory-1.2.2}/pyproject.toml +5 -4
  5. yourmemory-1.2.2/src/services/extract.py +52 -0
  6. yourmemory-1.2.2/src/services/extract_fallback.py +34 -0
  7. yourmemory-1.2.2/src/services/resolve_fallback.py +192 -0
  8. {yourmemory-1.2.0 → yourmemory-1.2.2/yourmemory.egg-info}/PKG-INFO +70 -57
  9. {yourmemory-1.2.0 → yourmemory-1.2.2}/yourmemory.egg-info/SOURCES.txt +2 -0
  10. {yourmemory-1.2.0 → yourmemory-1.2.2}/yourmemory.egg-info/entry_points.txt +1 -0
  11. {yourmemory-1.2.0 → yourmemory-1.2.2}/yourmemory.egg-info/requires.txt +1 -1
  12. yourmemory-1.2.0/src/services/extract.py +0 -38
  13. {yourmemory-1.2.0 → yourmemory-1.2.2}/LICENSE +0 -0
  14. {yourmemory-1.2.0 → yourmemory-1.2.2}/setup.cfg +0 -0
  15. {yourmemory-1.2.0 → yourmemory-1.2.2}/src/__init__.py +0 -0
  16. {yourmemory-1.2.0 → yourmemory-1.2.2}/src/app.py +0 -0
  17. {yourmemory-1.2.0 → yourmemory-1.2.2}/src/db/connection.py +0 -0
  18. {yourmemory-1.2.0 → yourmemory-1.2.2}/src/db/duckdb_schema.sql +0 -0
  19. {yourmemory-1.2.0 → yourmemory-1.2.2}/src/db/migrate.py +0 -0
  20. {yourmemory-1.2.0 → yourmemory-1.2.2}/src/db/schema.sql +0 -0
  21. {yourmemory-1.2.0 → yourmemory-1.2.2}/src/db/sqlite_schema.sql +0 -0
  22. {yourmemory-1.2.0 → yourmemory-1.2.2}/src/jobs/decay_job.py +0 -0
  23. {yourmemory-1.2.0 → yourmemory-1.2.2}/src/routes/__init__.py +0 -0
  24. {yourmemory-1.2.0 → yourmemory-1.2.2}/src/routes/agents.py +0 -0
  25. {yourmemory-1.2.0 → yourmemory-1.2.2}/src/routes/memories.py +0 -0
  26. {yourmemory-1.2.0 → yourmemory-1.2.2}/src/routes/retrieve.py +0 -0
  27. {yourmemory-1.2.0 → yourmemory-1.2.2}/src/services/__init__.py +0 -0
  28. {yourmemory-1.2.0 → yourmemory-1.2.2}/src/services/agent_registry.py +0 -0
  29. {yourmemory-1.2.0 → yourmemory-1.2.2}/src/services/api_keys.py +0 -0
  30. {yourmemory-1.2.0 → yourmemory-1.2.2}/src/services/decay.py +0 -0
  31. {yourmemory-1.2.0 → yourmemory-1.2.2}/src/services/embed.py +0 -0
  32. {yourmemory-1.2.0 → yourmemory-1.2.2}/src/services/resolve.py +0 -0
  33. {yourmemory-1.2.0 → yourmemory-1.2.2}/src/services/retrieve.py +0 -0
  34. {yourmemory-1.2.0 → yourmemory-1.2.2}/yourmemory.egg-info/dependency_links.txt +0 -0
  35. {yourmemory-1.2.0 → yourmemory-1.2.2}/yourmemory.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yourmemory
3
- Version: 1.2.0
3
+ Version: 1.2.2
4
4
  Summary: Persistent memory for Claude — Ebbinghaus forgetting curve, semantic deduplication, MCP-native
5
5
  Author-email: Sachit Misra <mishrasachit1@gmail.com>
6
6
  License: Apache License
@@ -163,8 +163,8 @@ License: Apache License
163
163
  See the License for the specific language governing permissions and
164
164
  limitations under the License.
165
165
 
166
- Project-URL: Homepage, https://github.com/sachitrafa/cognitive-ai-memory
167
- Project-URL: Repository, https://github.com/sachitrafa/cognitive-ai-memory
166
+ Project-URL: Homepage, https://github.com/sachitrafa/YourMemory
167
+ Project-URL: Repository, https://github.com/sachitrafa/YourMemory
168
168
  Keywords: mcp,claude,memory,ebbinghaus,ai,sqlite,postgresql
169
169
  Classifier: Programming Language :: Python :: 3
170
170
  Classifier: Programming Language :: Python :: 3.11
@@ -181,7 +181,7 @@ Requires-Dist: numpy
181
181
  Requires-Dist: python-dateutil
182
182
  Requires-Dist: duckdb>=0.10.0
183
183
  Requires-Dist: apscheduler
184
- Requires-Dist: spacy<4.0,>=3.7
184
+ Requires-Dist: spacy<4.0,>=3.8.13
185
185
  Provides-Extra: postgres
186
186
  Requires-Dist: psycopg2-binary; extra == "postgres"
187
187
  Requires-Dist: pgvector; extra == "postgres"
@@ -197,7 +197,7 @@ Dynamic: license-file
197
197
 
198
198
  **+16pp better recall than Mem0 on LoCoMo. 100% stale memory precision. Biologically-inspired memory decay for AI agents.**
199
199
 
200
- Persistent memory for Claude that works like human memory important things stick, forgotten things fade, outdated facts get demoted automatically.
200
+ Persistent memory for Claude and any MCP-compatible AI — works like human memory. Important things stick, forgotten things fade, outdated facts get pruned automatically.
201
201
 
202
202
  > Early stage — feedback and ideas welcome.
203
203
 
@@ -244,7 +244,9 @@ Importance additionally modulates the decay rate within each category. Memories
244
244
 
245
245
  ## Setup
246
246
 
247
- **Zero infrastructure required** — uses SQLite out of the box. Two commands and you're done.
247
+ **Zero infrastructure required** — uses DuckDB out of the box. Two commands and you're done.
248
+
249
+ Supports **Python 3.11, 3.12, 3.13, and 3.14**.
248
250
 
249
251
  ### 1. Install
250
252
 
@@ -252,11 +254,21 @@ Importance additionally modulates the decay rate within each category. Memories
252
254
  pip install yourmemory
253
255
  ```
254
256
 
255
- All dependencies are installed automatically. No clone, no separate download steps needed.
257
+ All dependencies installed automatically. No clone, no Docker, no database setup.
258
+
259
+ ### 2. Get your config
260
+
261
+ Run this once to get your exact config:
256
262
 
257
- ### 2. Wire into your AI client
263
+ ```bash
264
+ yourmemory-path
265
+ ```
258
266
 
259
- The database is created automatically at `~/.yourmemory/memories.db` on first use. No `.env` file needed.
267
+ It prints your full executable path and a ready-to-paste config for any MCP client. Copy it.
268
+
269
+ ### 3. Wire into your AI client
270
+
271
+ The database is created automatically at `~/.yourmemory/memories.duckdb` on first use.
260
272
 
261
273
  #### Claude Code
262
274
 
@@ -276,26 +288,49 @@ Reload Claude Code (`Cmd+Shift+P` → `Developer: Reload Window`).
276
288
 
277
289
  #### Cline (VS Code)
278
290
 
279
- In Cline **MCP Servers** **Edit MCP Settings**:
291
+ VS Code doesn't inherit your shell PATH. Run this in terminal to get the exact config to paste:
292
+
293
+ ```bash
294
+ yourmemory-path
295
+ ```
296
+
297
+ Then in Cline → **MCP Servers** → **Edit MCP Settings**, paste the output. It looks like:
280
298
 
281
299
  ```json
282
300
  {
283
301
  "mcpServers": {
284
302
  "yourmemory": {
285
- "command": "yourmemory",
303
+ "command": "/full/path/to/yourmemory",
286
304
  "args": [],
287
305
  "env": {
288
- "DATABASE_URL": "",
289
- "YOURMEMORY_USER": "your_name"
306
+ "YOURMEMORY_USER": "your_name",
307
+ "DATABASE_URL": ""
290
308
  }
291
309
  }
292
310
  }
293
311
  }
294
312
  ```
295
313
 
296
- Replace `your_name` with your name. Save — Cline will connect automatically.
314
+ Restart Cline after saving.
315
+
316
+ #### Cursor
297
317
 
298
- > **Note:** If Cline can't find the `yourmemory` command, run `which yourmemory` in terminal to get the full path and use that instead.
318
+ Add to `~/.cursor/mcp.json`:
319
+
320
+ ```json
321
+ {
322
+ "mcpServers": {
323
+ "yourmemory": {
324
+ "command": "/full/path/to/yourmemory",
325
+ "args": [],
326
+ "env": {
327
+ "YOURMEMORY_USER": "your_name",
328
+ "DATABASE_URL": ""
329
+ }
330
+ }
331
+ }
332
+ }
333
+ ```
299
334
 
300
335
  #### Claude Desktop
301
336
 
@@ -315,9 +350,9 @@ Restart Claude Desktop.
315
350
 
316
351
  #### Any MCP-compatible client
317
352
 
318
- YourMemory is a standard stdio MCP server. The command is simply `yourmemory`. Add it to any client that supports MCP servers using the same pattern above.
353
+ YourMemory is a standard stdio MCP server. Works with Claude Code, Claude Desktop, Cline, Cursor, Windsurf, Continue, and Zed. Use the full path from `yourmemory-path` if the client doesn't inherit shell PATH.
319
354
 
320
- ### 3. Add memory instructions to your project
355
+ ### 4. Add memory instructions to your project
321
356
 
322
357
  Copy `sample_CLAUDE.md` into your project root as `CLAUDE.md` and replace:
323
358
  - `YOUR_NAME` — your name (e.g. `Alice`)
@@ -329,13 +364,19 @@ Claude will now follow the recall → store → update workflow automatically on
329
364
 
330
365
  ### PostgreSQL (optional — for teams or large datasets)
331
366
 
332
- If you have PostgreSQL + pgvector, create a `.env` file:
367
+ Install with Postgres support:
368
+
369
+ ```bash
370
+ pip install yourmemory[postgres]
371
+ ```
372
+
373
+ Then create a `.env` file:
333
374
 
334
375
  ```bash
335
376
  DATABASE_URL=postgresql://YOUR_USER@localhost:5432/yourmemory
336
377
  ```
337
378
 
338
- The backend is selected automatically — `postgresql://` in `DATABASE_URL` → Postgres + pgvector, anything else → SQLite.
379
+ The backend is selected automatically — `postgresql://` in `DATABASE_URL` → Postgres + pgvector, anything else → DuckDB.
339
380
 
340
381
  **macOS**
341
382
  ```bash
@@ -349,8 +390,6 @@ sudo apt install postgresql postgresql-contrib postgresql-16-pgvector
349
390
  createdb yourmemory
350
391
  ```
351
392
 
352
- > **One-liner setup script** (macOS/Linux): `bash scripts/setup_db.sh` handles install + DB creation automatically.
353
-
354
393
  ---
355
394
 
356
395
  ## MCP Tools
@@ -402,47 +441,21 @@ Runs automatically every 24 hours on startup — no cron needed. Memories below
402
441
 
403
442
  ---
404
443
 
405
- ## REST API
406
-
407
- ```bash
408
- # Store
409
- curl -X POST http://localhost:8000/memories \
410
- -H "Content-Type: application/json" \
411
- -d '{"userId":"u1","content":"Prefers dark mode","importance":0.8}'
412
-
413
- # Retrieve
414
- curl -X POST http://localhost:8000/retrieve \
415
- -H "Content-Type: application/json" \
416
- -d '{"userId":"u1","query":"UI preferences"}'
417
-
418
- # List all
419
- curl "http://localhost:8000/memories?userId=u1"
420
-
421
- # Update
422
- curl -X PUT http://localhost:8000/memories/42 \
423
- -H "Content-Type: application/json" \
424
- -d '{"content":"Prefers dark mode in all apps","importance":0.85}'
425
-
426
- # Delete
427
- curl -X DELETE http://localhost:8000/memories/42
428
- ```
429
-
430
- ---
431
-
432
444
  ## Stack
433
445
 
434
- - **PostgreSQL + pgvector** — vector similarity search
446
+ - **DuckDB** — default backend, zero setup, native vector similarity (same quality as pgvector)
435
447
  - **sentence-transformers** — local embeddings (`all-mpnet-base-v2`, 768 dims, no external service needed)
436
- - **FastAPI**REST server
448
+ - **spaCy 3.8.13+** local NLP for deduplication and categorization (Python 3.11–3.14 compatible)
437
449
  - **APScheduler** — automatic 24h decay job
438
450
  - **MCP** — Claude integration via Model Context Protocol
451
+ - **PostgreSQL + pgvector** — optional, for teams / large datasets
439
452
 
440
453
  ---
441
454
 
442
455
  ## Architecture
443
456
 
444
457
  ```
445
- Claude Code
458
+ Claude / Cline / Cursor / Any MCP client
446
459
 
447
460
  ├── recall_memory(query)
448
461
  │ └── embed → cosine similarity → score = sim × strength → top-k
@@ -455,12 +468,12 @@ Claude Code
455
468
  └── update_memory(id, new_content)
456
469
  └── embed(new_content) → UPDATE memories
457
470
 
458
- PostgreSQL (pgvector)
459
- └── memories
460
- ├── embedding vector(768)
461
- ├── importance float
462
- ├── recall_count int
463
- └── last_accessed_at
471
+ DuckDB (default) PostgreSQL + pgvector (optional)
472
+ └── memories.duckdb └── memories table
473
+ ├── embedding FLOAT[768] ├── embedding vector(768)
474
+ ├── importance FLOAT ├── importance float
475
+ ├── recall_count INTEGER ├── recall_count int
476
+ └── last_accessed_at └── last_accessed_at
464
477
  ```
465
478
 
466
479
  ---
@@ -2,7 +2,7 @@
2
2
 
3
3
  **+16pp better recall than Mem0 on LoCoMo. 100% stale memory precision. Biologically-inspired memory decay for AI agents.**
4
4
 
5
- Persistent memory for Claude that works like human memory important things stick, forgotten things fade, outdated facts get demoted automatically.
5
+ Persistent memory for Claude and any MCP-compatible AI — works like human memory. Important things stick, forgotten things fade, outdated facts get pruned automatically.
6
6
 
7
7
  > Early stage — feedback and ideas welcome.
8
8
 
@@ -49,7 +49,9 @@ Importance additionally modulates the decay rate within each category. Memories
49
49
 
50
50
  ## Setup
51
51
 
52
- **Zero infrastructure required** — uses SQLite out of the box. Two commands and you're done.
52
+ **Zero infrastructure required** — uses DuckDB out of the box. Two commands and you're done.
53
+
54
+ Supports **Python 3.11, 3.12, 3.13, and 3.14**.
53
55
 
54
56
  ### 1. Install
55
57
 
@@ -57,11 +59,21 @@ Importance additionally modulates the decay rate within each category. Memories
57
59
  pip install yourmemory
58
60
  ```
59
61
 
60
- All dependencies are installed automatically. No clone, no separate download steps needed.
62
+ All dependencies installed automatically. No clone, no Docker, no database setup.
63
+
64
+ ### 2. Get your config
65
+
66
+ Run this once to get your exact config:
61
67
 
62
- ### 2. Wire into your AI client
68
+ ```bash
69
+ yourmemory-path
70
+ ```
63
71
 
64
- The database is created automatically at `~/.yourmemory/memories.db` on first use. No `.env` file needed.
72
+ It prints your full executable path and a ready-to-paste config for any MCP client. Copy it.
73
+
74
+ ### 3. Wire into your AI client
75
+
76
+ The database is created automatically at `~/.yourmemory/memories.duckdb` on first use.
65
77
 
66
78
  #### Claude Code
67
79
 
@@ -81,26 +93,49 @@ Reload Claude Code (`Cmd+Shift+P` → `Developer: Reload Window`).
81
93
 
82
94
  #### Cline (VS Code)
83
95
 
84
- In Cline **MCP Servers** **Edit MCP Settings**:
96
+ VS Code doesn't inherit your shell PATH. Run this in terminal to get the exact config to paste:
97
+
98
+ ```bash
99
+ yourmemory-path
100
+ ```
101
+
102
+ Then in Cline → **MCP Servers** → **Edit MCP Settings**, paste the output. It looks like:
85
103
 
86
104
  ```json
87
105
  {
88
106
  "mcpServers": {
89
107
  "yourmemory": {
90
- "command": "yourmemory",
108
+ "command": "/full/path/to/yourmemory",
91
109
  "args": [],
92
110
  "env": {
93
- "DATABASE_URL": "",
94
- "YOURMEMORY_USER": "your_name"
111
+ "YOURMEMORY_USER": "your_name",
112
+ "DATABASE_URL": ""
95
113
  }
96
114
  }
97
115
  }
98
116
  }
99
117
  ```
100
118
 
101
- Replace `your_name` with your name. Save — Cline will connect automatically.
119
+ Restart Cline after saving.
120
+
121
+ #### Cursor
102
122
 
103
- > **Note:** If Cline can't find the `yourmemory` command, run `which yourmemory` in terminal to get the full path and use that instead.
123
+ Add to `~/.cursor/mcp.json`:
124
+
125
+ ```json
126
+ {
127
+ "mcpServers": {
128
+ "yourmemory": {
129
+ "command": "/full/path/to/yourmemory",
130
+ "args": [],
131
+ "env": {
132
+ "YOURMEMORY_USER": "your_name",
133
+ "DATABASE_URL": ""
134
+ }
135
+ }
136
+ }
137
+ }
138
+ ```
104
139
 
105
140
  #### Claude Desktop
106
141
 
@@ -120,9 +155,9 @@ Restart Claude Desktop.
120
155
 
121
156
  #### Any MCP-compatible client
122
157
 
123
- YourMemory is a standard stdio MCP server. The command is simply `yourmemory`. Add it to any client that supports MCP servers using the same pattern above.
158
+ YourMemory is a standard stdio MCP server. Works with Claude Code, Claude Desktop, Cline, Cursor, Windsurf, Continue, and Zed. Use the full path from `yourmemory-path` if the client doesn't inherit shell PATH.
124
159
 
125
- ### 3. Add memory instructions to your project
160
+ ### 4. Add memory instructions to your project
126
161
 
127
162
  Copy `sample_CLAUDE.md` into your project root as `CLAUDE.md` and replace:
128
163
  - `YOUR_NAME` — your name (e.g. `Alice`)
@@ -134,13 +169,19 @@ Claude will now follow the recall → store → update workflow automatically on
134
169
 
135
170
  ### PostgreSQL (optional — for teams or large datasets)
136
171
 
137
- If you have PostgreSQL + pgvector, create a `.env` file:
172
+ Install with Postgres support:
173
+
174
+ ```bash
175
+ pip install yourmemory[postgres]
176
+ ```
177
+
178
+ Then create a `.env` file:
138
179
 
139
180
  ```bash
140
181
  DATABASE_URL=postgresql://YOUR_USER@localhost:5432/yourmemory
141
182
  ```
142
183
 
143
- The backend is selected automatically — `postgresql://` in `DATABASE_URL` → Postgres + pgvector, anything else → SQLite.
184
+ The backend is selected automatically — `postgresql://` in `DATABASE_URL` → Postgres + pgvector, anything else → DuckDB.
144
185
 
145
186
  **macOS**
146
187
  ```bash
@@ -154,8 +195,6 @@ sudo apt install postgresql postgresql-contrib postgresql-16-pgvector
154
195
  createdb yourmemory
155
196
  ```
156
197
 
157
- > **One-liner setup script** (macOS/Linux): `bash scripts/setup_db.sh` handles install + DB creation automatically.
158
-
159
198
  ---
160
199
 
161
200
  ## MCP Tools
@@ -207,47 +246,21 @@ Runs automatically every 24 hours on startup — no cron needed. Memories below
207
246
 
208
247
  ---
209
248
 
210
- ## REST API
211
-
212
- ```bash
213
- # Store
214
- curl -X POST http://localhost:8000/memories \
215
- -H "Content-Type: application/json" \
216
- -d '{"userId":"u1","content":"Prefers dark mode","importance":0.8}'
217
-
218
- # Retrieve
219
- curl -X POST http://localhost:8000/retrieve \
220
- -H "Content-Type: application/json" \
221
- -d '{"userId":"u1","query":"UI preferences"}'
222
-
223
- # List all
224
- curl "http://localhost:8000/memories?userId=u1"
225
-
226
- # Update
227
- curl -X PUT http://localhost:8000/memories/42 \
228
- -H "Content-Type: application/json" \
229
- -d '{"content":"Prefers dark mode in all apps","importance":0.85}'
230
-
231
- # Delete
232
- curl -X DELETE http://localhost:8000/memories/42
233
- ```
234
-
235
- ---
236
-
237
249
  ## Stack
238
250
 
239
- - **PostgreSQL + pgvector** — vector similarity search
251
+ - **DuckDB** — default backend, zero setup, native vector similarity (same quality as pgvector)
240
252
  - **sentence-transformers** — local embeddings (`all-mpnet-base-v2`, 768 dims, no external service needed)
241
- - **FastAPI**REST server
253
+ - **spaCy 3.8.13+** local NLP for deduplication and categorization (Python 3.11–3.14 compatible)
242
254
  - **APScheduler** — automatic 24h decay job
243
255
  - **MCP** — Claude integration via Model Context Protocol
256
+ - **PostgreSQL + pgvector** — optional, for teams / large datasets
244
257
 
245
258
  ---
246
259
 
247
260
  ## Architecture
248
261
 
249
262
  ```
250
- Claude Code
263
+ Claude / Cline / Cursor / Any MCP client
251
264
 
252
265
  ├── recall_memory(query)
253
266
  │ └── embed → cosine similarity → score = sim × strength → top-k
@@ -260,12 +273,12 @@ Claude Code
260
273
  └── update_memory(id, new_content)
261
274
  └── embed(new_content) → UPDATE memories
262
275
 
263
- PostgreSQL (pgvector)
264
- └── memories
265
- ├── embedding vector(768)
266
- ├── importance float
267
- ├── recall_count int
268
- └── last_accessed_at
276
+ DuckDB (default) PostgreSQL + pgvector (optional)
277
+ └── memories.duckdb └── memories table
278
+ ├── embedding FLOAT[768] ├── embedding vector(768)
279
+ ├── importance FLOAT ├── importance float
280
+ ├── recall_count INTEGER ├── recall_count int
281
+ └── last_accessed_at └── last_accessed_at
269
282
  ```
270
283
 
271
284
  ---
@@ -563,6 +563,40 @@ def print_path():
563
563
  print("Paste this into your Cline MCP settings:\n")
564
564
  print(_json.dumps(config, indent=2))
565
565
 
566
+ def setup():
567
+ """Run once after pip install to download the spaCy model."""
568
+ import subprocess
569
+ print("YourMemory setup — installing spaCy language model...")
570
+ result = subprocess.run(
571
+ [sys.executable, "-m", "spacy", "download", "en_core_web_sm"],
572
+ check=False,
573
+ )
574
+ if result.returncode == 0:
575
+ print("✓ spaCy model installed successfully.")
576
+ else:
577
+ # Fallback: install via direct wheel URL
578
+ print("Direct download fallback...")
579
+ result2 = subprocess.run(
580
+ [sys.executable, "-m", "pip", "install",
581
+ "https://github.com/explosion/spacy-models/releases/download/"
582
+ "en_core_web_sm-3.8.0/en_core_web_sm-3.8.0-py3-none-any.whl",
583
+ "--break-system-packages"],
584
+ check=False,
585
+ )
586
+ if result2.returncode == 0:
587
+ print("✓ spaCy model installed successfully.")
588
+ else:
589
+ print("✗ Could not install spaCy model automatically.")
590
+ print(" Run manually: python -m spacy download en_core_web_sm")
591
+ print(" YourMemory will still work using the built-in regex fallback.")
592
+
593
+ # Also run DB migration
594
+ from src.db.migrate import migrate
595
+ migrate()
596
+ print("✓ Database initialised.")
597
+ print("\nSetup complete. Run yourmemory-path to get your MCP config.")
598
+
599
+
566
600
  def run():
567
601
  from src.db.migrate import migrate
568
602
  migrate()
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "yourmemory"
7
- version = "1.2.0"
7
+ version = "1.2.2"
8
8
  description = "Persistent memory for Claude — Ebbinghaus forgetting curve, semantic deduplication, MCP-native"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -28,7 +28,7 @@ dependencies = [
28
28
  "python-dateutil",
29
29
  "duckdb>=0.10.0",
30
30
  "apscheduler",
31
- "spacy>=3.7,<4.0",
31
+ "spacy>=3.8.13,<4.0",
32
32
  ]
33
33
 
34
34
  [project.optional-dependencies]
@@ -39,10 +39,11 @@ all = ["yourmemory[postgres,sse]"]
39
39
  [project.scripts]
40
40
  yourmemory = "memory_mcp:run"
41
41
  yourmemory-path = "memory_mcp:print_path"
42
+ yourmemory-setup = "memory_mcp:setup"
42
43
 
43
44
  [project.urls]
44
- Homepage = "https://github.com/sachitrafa/cognitive-ai-memory"
45
- Repository = "https://github.com/sachitrafa/cognitive-ai-memory"
45
+ Homepage = "https://github.com/sachitrafa/YourMemory"
46
+ Repository = "https://github.com/sachitrafa/YourMemory"
46
47
 
47
48
  [tool.setuptools]
48
49
  py-modules = ["memory_mcp"]
@@ -0,0 +1,52 @@
1
+ import re
2
+ import sys
3
+
4
+ _QUESTION_WORDS = {"what", "who", "where", "when", "why", "how", "which", "whose", "whom"}
5
+
6
+ _IMPERATIVE_PATTERNS = [
7
+ r'^(please|use|try|do|don\'t|make|create|add|remove|delete|update)',
8
+ r'^(convert|transform|change|modify|fix|help|show|tell)',
9
+ r'^(install|run|execute|start|stop|restart|configure)',
10
+ ]
11
+
12
+ # Load spaCy if available — falls back to regex if model not installed yet
13
+ # Run `yourmemory-setup` once after pip install to download the model
14
+ _nlp = None
15
+ try:
16
+ import spacy
17
+ _nlp = spacy.load("en_core_web_sm")
18
+ except OSError:
19
+ print(
20
+ "YourMemory: spaCy model not found. Run `yourmemory-setup` once to install it.\n"
21
+ " Falling back to built-in regex categorization.",
22
+ file=sys.stderr,
23
+ )
24
+ except Exception:
25
+ pass
26
+
27
+
28
+ def is_question(text: str) -> bool:
29
+ """Return True if the text is a question — questions are not stored as memories."""
30
+ stripped = text.strip()
31
+ if stripped.endswith("?"):
32
+ return True
33
+ first_word = re.split(r"\s+", stripped.lower())[0]
34
+ return first_word in _QUESTION_WORDS
35
+
36
+
37
+ def categorize(text: str) -> str:
38
+ """
39
+ Classify text as fact or assumption.
40
+ Uses spaCy dependency parse when available, regex heuristics otherwise.
41
+ Run `yourmemory-setup` to enable spaCy.
42
+ """
43
+ if _nlp is not None:
44
+ doc = _nlp(text)
45
+ has_subject = any(tok.dep_ in ("nsubj", "nsubjpass") for tok in doc)
46
+ return "fact" if has_subject else "assumption"
47
+
48
+ text_lower = text.lower().strip()
49
+ for pattern in _IMPERATIVE_PATTERNS:
50
+ if re.match(pattern, text_lower):
51
+ return "assumption"
52
+ return "fact"
@@ -0,0 +1,34 @@
1
+ import re
2
+
3
+ _QUESTION_WORDS = {"what", "who", "where", "when", "why", "how", "which", "whose", "whom"}
4
+
5
+
6
+ def is_question(text: str) -> bool:
7
+ """Return True if the text is a question — questions are not stored as memories."""
8
+ stripped = text.strip()
9
+ if stripped.endswith("?"):
10
+ return True
11
+ first_word = re.split(r"\s+", stripped.lower())[0]
12
+ return first_word in _QUESTION_WORDS
13
+
14
+
15
+ def categorize(text: str) -> str:
16
+ """
17
+ Fallback categorization without spaCy:
18
+ Simple heuristics to classify as fact vs assumption
19
+ """
20
+ text_lower = text.lower().strip()
21
+
22
+ # Commands/imperatives typically start with verbs or "please"
23
+ imperative_patterns = [
24
+ r'^(please|use|try|do|don\'t|make|create|add|remove|delete|update)',
25
+ r'^(convert|transform|change|modify|fix|help|show|tell)',
26
+ r'^(install|run|execute|start|stop|restart|configure)'
27
+ ]
28
+
29
+ for pattern in imperative_patterns:
30
+ if re.match(pattern, text_lower):
31
+ return "assumption"
32
+
33
+ # Default to fact for declarative statements
34
+ return "fact"
@@ -0,0 +1,192 @@
1
+ """
2
+ Semantic deduplication for POST /memories - Fallback version without spaCy.
3
+
4
+ Detects near-duplicate memories via cosine similarity and applies one of:
5
+ - reinforce : sim ≥ 0.85 — paraphrase, bump recall_count only
6
+ - replace : 0.65–0.85 + contradiction detected — overwrite with incoming
7
+ - merge : 0.65–0.85 + no contradiction — entity-append to existing
8
+ - new : sim < 0.65 — genuinely distinct, plain INSERT
9
+ """
10
+
11
+ import json
12
+ import math
13
+ import re
14
+ from src.db.connection import get_backend
15
+
16
+ DEDUP_THRESHOLD = 0.65 # below → always new memory
17
+ REINFORCE_THRESHOLD = 0.85 # at or above → reinforce (near-identical paraphrase)
18
+
19
+ # Simple contradiction detection patterns (fallback)
20
+ _CONTRADICTION_PATTERNS = [
21
+ (r'\b(love|like|prefer|enjoy)\b', r'\b(hate|dislike|avoid)\b'),
22
+ (r'\b(start|begin|use)\b', r'\b(stop|quit|avoid)\b'),
23
+ (r'\b(want|need)\b', r'\b(refuse|reject)\b'),
24
+ (r'\b(good|great|excellent)\b', r'\b(bad|terrible|awful)\b'),
25
+ (r'\b(yes|true|correct)\b', r'\b(no|false|wrong)\b'),
26
+ ]
27
+
28
+
29
+ def _cosine(a: list, b: list) -> float:
30
+ import numpy as np
31
+ va, vb = np.array(a, dtype=float), np.array(b, dtype=float)
32
+ denom = np.linalg.norm(va) * np.linalg.norm(vb)
33
+ return float(np.dot(va, vb) / denom) if denom else 0.0
34
+
35
+
36
+ def find_near_duplicate(user_id: str, embedding: list, conn) -> dict | None:
37
+ """
38
+ Return the closest existing memory if cosine similarity >= DEDUP_THRESHOLD,
39
+ else None. Uses the caller's open connection.
40
+ """
41
+ backend = get_backend()
42
+
43
+ if backend == "postgres":
44
+ embedding_str = f"[{','.join(str(x) for x in embedding)}]"
45
+ cur = conn.cursor()
46
+ cur.execute("""
47
+ SELECT id, content, category, importance, recall_count,
48
+ 1 - (embedding <=> %s::vector) AS similarity
49
+ FROM memories
50
+ WHERE user_id = %s
51
+ ORDER BY embedding <=> %s::vector
52
+ LIMIT 1
53
+ """, (embedding_str, user_id, embedding_str))
54
+ row = cur.fetchone()
55
+ cur.close()
56
+ if row is None:
57
+ return None
58
+ sim = row[5]
59
+ if sim < DEDUP_THRESHOLD:
60
+ return None
61
+ return {"id": row[0], "content": row[1], "category": row[2],
62
+ "importance": row[3], "recall_count": row[4], "similarity": sim}
63
+
64
+ if backend == "duckdb":
65
+ from src.db.connection import duckdb_row
66
+ cur = conn.execute("""
67
+ SELECT id, content, category, importance, recall_count,
68
+ array_cosine_similarity(embedding, ?::FLOAT[768]) AS similarity
69
+ FROM memories
70
+ WHERE user_id = ?
71
+ ORDER BY similarity DESC
72
+ LIMIT 1
73
+ """, [embedding, user_id])
74
+ row = duckdb_row(cur)
75
+ if row is None or row["similarity"] < DEDUP_THRESHOLD:
76
+ return None
77
+ return row
78
+
79
+ # SQLite: numpy cosine over all user memories
80
+ cur = conn.cursor()
81
+ cur.execute("""
82
+ SELECT id, content, category, importance, recall_count, embedding
83
+ FROM memories WHERE user_id = ?
84
+ """, (user_id,))
85
+ rows = cur.fetchall()
86
+ cur.close()
87
+
88
+ best, sim = None, -1.0
89
+ for row in rows:
90
+ raw = row[5] if isinstance(row, tuple) else row["embedding"]
91
+ if raw is None:
92
+ continue
93
+ s = _cosine(embedding, json.loads(raw))
94
+ if s > sim:
95
+ sim, best = s, row
96
+ if best is None or sim < DEDUP_THRESHOLD:
97
+ return None
98
+ return {"id": best[0], "content": best[1], "category": best[2],
99
+ "importance": best[3], "recall_count": best[4], "similarity": sim}
100
+
101
+
102
+ def detect_contradiction(existing_text: str, incoming_text: str) -> bool:
103
+ """
104
+ Fallback contradiction detection using regex patterns.
105
+ Return True if the incoming text contradicts the existing one.
106
+ """
107
+ existing_lower = existing_text.lower()
108
+ incoming_lower = incoming_text.lower()
109
+
110
+ for positive_pattern, negative_pattern in _CONTRADICTION_PATTERNS:
111
+ # Check if existing has positive and incoming has negative
112
+ if re.search(positive_pattern, existing_lower) and re.search(negative_pattern, incoming_lower):
113
+ return True
114
+ # Check if existing has negative and incoming has positive
115
+ if re.search(negative_pattern, existing_lower) and re.search(positive_pattern, incoming_lower):
116
+ return True
117
+
118
+ return False
119
+
120
+
121
+ def merge_entities(existing_text: str, incoming_text: str) -> str:
122
+ """
123
+ Fallback entity merging using simple heuristics.
124
+ Append capitalized words and quoted strings from incoming that are absent from existing.
125
+ Returns the merged string, or existing_text unchanged if nothing new found.
126
+ """
127
+ existing_lower = existing_text.lower()
128
+
129
+ # Extract potential entities using simple patterns
130
+ candidates = []
131
+
132
+ # Capitalized words (potential proper nouns)
133
+ capitalized_words = re.findall(r'\b[A-Z][a-zA-Z]{2,}\b', incoming_text)
134
+ candidates.extend(capitalized_words)
135
+
136
+ # Quoted strings
137
+ quoted_strings = re.findall(r'"([^"]+)"', incoming_text)
138
+ quoted_strings.extend(re.findall(r"'([^']+)'", incoming_text))
139
+ candidates.extend(quoted_strings)
140
+
141
+ # Technical terms (words with numbers, dots, underscores)
142
+ tech_terms = re.findall(r'\b[a-zA-Z][a-zA-Z0-9._-]*[a-zA-Z0-9]\b', incoming_text)
143
+ candidates.extend([t for t in tech_terms if '.' in t or '_' in t or any(c.isdigit() for c in t)])
144
+
145
+ # Filter out terms already present in existing text
146
+ new_terms = [t for t in candidates if t.lower() not in existing_lower and len(t.strip()) > 2]
147
+
148
+ # Deduplicate while preserving order
149
+ seen, deduped = set(), []
150
+ for t in new_terms:
151
+ if t.lower() not in seen:
152
+ seen.add(t.lower())
153
+ deduped.append(t)
154
+
155
+ if not deduped:
156
+ return existing_text
157
+ if len(deduped) == 1:
158
+ return f"{existing_text} with {deduped[0]}"
159
+ return f"{existing_text} with {', '.join(deduped[:-1])} and {deduped[-1]}"
160
+
161
+
162
+ def resolve(user_id: str, content: str, embedding: list, conn) -> dict:
163
+ """
164
+ Facade: decide what to do with an incoming memory.
165
+
166
+ Returns:
167
+ {
168
+ "action": "new" | "reinforce" | "replace" | "merge",
169
+ "content": str, # final content to store/update
170
+ "existing": dict | None, # matched row if any
171
+ }
172
+ """
173
+ match = find_near_duplicate(user_id, embedding, conn)
174
+
175
+ if match is None:
176
+ return {"action": "new", "content": content, "existing": None}
177
+
178
+ sim = match["similarity"]
179
+
180
+ if sim >= REINFORCE_THRESHOLD:
181
+ return {"action": "reinforce", "content": match["content"], "existing": match}
182
+
183
+ # DEDUP_THRESHOLD ≤ sim < REINFORCE_THRESHOLD
184
+ if detect_contradiction(match["content"], content):
185
+ return {"action": "replace", "content": content, "existing": match}
186
+
187
+ merged = merge_entities(match["content"], content)
188
+ if merged == match["content"]:
189
+ # No new entities found — treat as paraphrase
190
+ return {"action": "reinforce", "content": match["content"], "existing": match}
191
+
192
+ return {"action": "merge", "content": merged, "existing": match}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yourmemory
3
- Version: 1.2.0
3
+ Version: 1.2.2
4
4
  Summary: Persistent memory for Claude — Ebbinghaus forgetting curve, semantic deduplication, MCP-native
5
5
  Author-email: Sachit Misra <mishrasachit1@gmail.com>
6
6
  License: Apache License
@@ -163,8 +163,8 @@ License: Apache License
163
163
  See the License for the specific language governing permissions and
164
164
  limitations under the License.
165
165
 
166
- Project-URL: Homepage, https://github.com/sachitrafa/cognitive-ai-memory
167
- Project-URL: Repository, https://github.com/sachitrafa/cognitive-ai-memory
166
+ Project-URL: Homepage, https://github.com/sachitrafa/YourMemory
167
+ Project-URL: Repository, https://github.com/sachitrafa/YourMemory
168
168
  Keywords: mcp,claude,memory,ebbinghaus,ai,sqlite,postgresql
169
169
  Classifier: Programming Language :: Python :: 3
170
170
  Classifier: Programming Language :: Python :: 3.11
@@ -181,7 +181,7 @@ Requires-Dist: numpy
181
181
  Requires-Dist: python-dateutil
182
182
  Requires-Dist: duckdb>=0.10.0
183
183
  Requires-Dist: apscheduler
184
- Requires-Dist: spacy<4.0,>=3.7
184
+ Requires-Dist: spacy<4.0,>=3.8.13
185
185
  Provides-Extra: postgres
186
186
  Requires-Dist: psycopg2-binary; extra == "postgres"
187
187
  Requires-Dist: pgvector; extra == "postgres"
@@ -197,7 +197,7 @@ Dynamic: license-file
197
197
 
198
198
  **+16pp better recall than Mem0 on LoCoMo. 100% stale memory precision. Biologically-inspired memory decay for AI agents.**
199
199
 
200
- Persistent memory for Claude that works like human memory important things stick, forgotten things fade, outdated facts get demoted automatically.
200
+ Persistent memory for Claude and any MCP-compatible AI — works like human memory. Important things stick, forgotten things fade, outdated facts get pruned automatically.
201
201
 
202
202
  > Early stage — feedback and ideas welcome.
203
203
 
@@ -244,7 +244,9 @@ Importance additionally modulates the decay rate within each category. Memories
244
244
 
245
245
  ## Setup
246
246
 
247
- **Zero infrastructure required** — uses SQLite out of the box. Two commands and you're done.
247
+ **Zero infrastructure required** — uses DuckDB out of the box. Two commands and you're done.
248
+
249
+ Supports **Python 3.11, 3.12, 3.13, and 3.14**.
248
250
 
249
251
  ### 1. Install
250
252
 
@@ -252,11 +254,21 @@ Importance additionally modulates the decay rate within each category. Memories
252
254
  pip install yourmemory
253
255
  ```
254
256
 
255
- All dependencies are installed automatically. No clone, no separate download steps needed.
257
+ All dependencies installed automatically. No clone, no Docker, no database setup.
258
+
259
+ ### 2. Get your config
260
+
261
+ Run this once to get your exact config:
256
262
 
257
- ### 2. Wire into your AI client
263
+ ```bash
264
+ yourmemory-path
265
+ ```
258
266
 
259
- The database is created automatically at `~/.yourmemory/memories.db` on first use. No `.env` file needed.
267
+ It prints your full executable path and a ready-to-paste config for any MCP client. Copy it.
268
+
269
+ ### 3. Wire into your AI client
270
+
271
+ The database is created automatically at `~/.yourmemory/memories.duckdb` on first use.
260
272
 
261
273
  #### Claude Code
262
274
 
@@ -276,26 +288,49 @@ Reload Claude Code (`Cmd+Shift+P` → `Developer: Reload Window`).
276
288
 
277
289
  #### Cline (VS Code)
278
290
 
279
- In Cline **MCP Servers** **Edit MCP Settings**:
291
+ VS Code doesn't inherit your shell PATH. Run this in terminal to get the exact config to paste:
292
+
293
+ ```bash
294
+ yourmemory-path
295
+ ```
296
+
297
+ Then in Cline → **MCP Servers** → **Edit MCP Settings**, paste the output. It looks like:
280
298
 
281
299
  ```json
282
300
  {
283
301
  "mcpServers": {
284
302
  "yourmemory": {
285
- "command": "yourmemory",
303
+ "command": "/full/path/to/yourmemory",
286
304
  "args": [],
287
305
  "env": {
288
- "DATABASE_URL": "",
289
- "YOURMEMORY_USER": "your_name"
306
+ "YOURMEMORY_USER": "your_name",
307
+ "DATABASE_URL": ""
290
308
  }
291
309
  }
292
310
  }
293
311
  }
294
312
  ```
295
313
 
296
- Replace `your_name` with your name. Save — Cline will connect automatically.
314
+ Restart Cline after saving.
315
+
316
+ #### Cursor
297
317
 
298
- > **Note:** If Cline can't find the `yourmemory` command, run `which yourmemory` in terminal to get the full path and use that instead.
318
+ Add to `~/.cursor/mcp.json`:
319
+
320
+ ```json
321
+ {
322
+ "mcpServers": {
323
+ "yourmemory": {
324
+ "command": "/full/path/to/yourmemory",
325
+ "args": [],
326
+ "env": {
327
+ "YOURMEMORY_USER": "your_name",
328
+ "DATABASE_URL": ""
329
+ }
330
+ }
331
+ }
332
+ }
333
+ ```
299
334
 
300
335
  #### Claude Desktop
301
336
 
@@ -315,9 +350,9 @@ Restart Claude Desktop.
315
350
 
316
351
  #### Any MCP-compatible client
317
352
 
318
- YourMemory is a standard stdio MCP server. The command is simply `yourmemory`. Add it to any client that supports MCP servers using the same pattern above.
353
+ YourMemory is a standard stdio MCP server. Works with Claude Code, Claude Desktop, Cline, Cursor, Windsurf, Continue, and Zed. Use the full path from `yourmemory-path` if the client doesn't inherit shell PATH.
319
354
 
320
- ### 3. Add memory instructions to your project
355
+ ### 4. Add memory instructions to your project
321
356
 
322
357
  Copy `sample_CLAUDE.md` into your project root as `CLAUDE.md` and replace:
323
358
  - `YOUR_NAME` — your name (e.g. `Alice`)
@@ -329,13 +364,19 @@ Claude will now follow the recall → store → update workflow automatically on
329
364
 
330
365
  ### PostgreSQL (optional — for teams or large datasets)
331
366
 
332
- If you have PostgreSQL + pgvector, create a `.env` file:
367
+ Install with Postgres support:
368
+
369
+ ```bash
370
+ pip install yourmemory[postgres]
371
+ ```
372
+
373
+ Then create a `.env` file:
333
374
 
334
375
  ```bash
335
376
  DATABASE_URL=postgresql://YOUR_USER@localhost:5432/yourmemory
336
377
  ```
337
378
 
338
- The backend is selected automatically — `postgresql://` in `DATABASE_URL` → Postgres + pgvector, anything else → SQLite.
379
+ The backend is selected automatically — `postgresql://` in `DATABASE_URL` → Postgres + pgvector, anything else → DuckDB.
339
380
 
340
381
  **macOS**
341
382
  ```bash
@@ -349,8 +390,6 @@ sudo apt install postgresql postgresql-contrib postgresql-16-pgvector
349
390
  createdb yourmemory
350
391
  ```
351
392
 
352
- > **One-liner setup script** (macOS/Linux): `bash scripts/setup_db.sh` handles install + DB creation automatically.
353
-
354
393
  ---
355
394
 
356
395
  ## MCP Tools
@@ -402,47 +441,21 @@ Runs automatically every 24 hours on startup — no cron needed. Memories below
402
441
 
403
442
  ---
404
443
 
405
- ## REST API
406
-
407
- ```bash
408
- # Store
409
- curl -X POST http://localhost:8000/memories \
410
- -H "Content-Type: application/json" \
411
- -d '{"userId":"u1","content":"Prefers dark mode","importance":0.8}'
412
-
413
- # Retrieve
414
- curl -X POST http://localhost:8000/retrieve \
415
- -H "Content-Type: application/json" \
416
- -d '{"userId":"u1","query":"UI preferences"}'
417
-
418
- # List all
419
- curl "http://localhost:8000/memories?userId=u1"
420
-
421
- # Update
422
- curl -X PUT http://localhost:8000/memories/42 \
423
- -H "Content-Type: application/json" \
424
- -d '{"content":"Prefers dark mode in all apps","importance":0.85}'
425
-
426
- # Delete
427
- curl -X DELETE http://localhost:8000/memories/42
428
- ```
429
-
430
- ---
431
-
432
444
  ## Stack
433
445
 
434
- - **PostgreSQL + pgvector** — vector similarity search
446
+ - **DuckDB** — default backend, zero setup, native vector similarity (same quality as pgvector)
435
447
  - **sentence-transformers** — local embeddings (`all-mpnet-base-v2`, 768 dims, no external service needed)
436
- - **FastAPI**REST server
448
+ - **spaCy 3.8.13+** local NLP for deduplication and categorization (Python 3.11–3.14 compatible)
437
449
  - **APScheduler** — automatic 24h decay job
438
450
  - **MCP** — Claude integration via Model Context Protocol
451
+ - **PostgreSQL + pgvector** — optional, for teams / large datasets
439
452
 
440
453
  ---
441
454
 
442
455
  ## Architecture
443
456
 
444
457
  ```
445
- Claude Code
458
+ Claude / Cline / Cursor / Any MCP client
446
459
 
447
460
  ├── recall_memory(query)
448
461
  │ └── embed → cosine similarity → score = sim × strength → top-k
@@ -455,12 +468,12 @@ Claude Code
455
468
  └── update_memory(id, new_content)
456
469
  └── embed(new_content) → UPDATE memories
457
470
 
458
- PostgreSQL (pgvector)
459
- └── memories
460
- ├── embedding vector(768)
461
- ├── importance float
462
- ├── recall_count int
463
- └── last_accessed_at
471
+ DuckDB (default) PostgreSQL + pgvector (optional)
472
+ └── memories.duckdb └── memories table
473
+ ├── embedding FLOAT[768] ├── embedding vector(768)
474
+ ├── importance FLOAT ├── importance float
475
+ ├── recall_count INTEGER ├── recall_count int
476
+ └── last_accessed_at └── last_accessed_at
464
477
  ```
465
478
 
466
479
  ---
@@ -20,7 +20,9 @@ src/services/api_keys.py
20
20
  src/services/decay.py
21
21
  src/services/embed.py
22
22
  src/services/extract.py
23
+ src/services/extract_fallback.py
23
24
  src/services/resolve.py
25
+ src/services/resolve_fallback.py
24
26
  src/services/retrieve.py
25
27
  yourmemory.egg-info/PKG-INFO
26
28
  yourmemory.egg-info/SOURCES.txt
@@ -1,3 +1,4 @@
1
1
  [console_scripts]
2
2
  yourmemory = memory_mcp:run
3
3
  yourmemory-path = memory_mcp:print_path
4
+ yourmemory-setup = memory_mcp:setup
@@ -5,7 +5,7 @@ numpy
5
5
  python-dateutil
6
6
  duckdb>=0.10.0
7
7
  apscheduler
8
- spacy<4.0,>=3.7
8
+ spacy<4.0,>=3.8.13
9
9
 
10
10
  [all]
11
11
  yourmemory[postgres,sse]
@@ -1,38 +0,0 @@
1
- import re
2
- import spacy
3
-
4
- try:
5
- _nlp = spacy.load("en_core_web_sm")
6
- except OSError:
7
- import subprocess, sys
8
- subprocess.run(
9
- [sys.executable, "-m", "pip", "install",
10
- "https://github.com/explosion/spacy-models/releases/download/"
11
- "en_core_web_sm-3.8.0/en_core_web_sm-3.8.0-py3-none-any.whl"],
12
- check=True,
13
- )
14
- _nlp = spacy.load("en_core_web_sm")
15
-
16
- _QUESTION_WORDS = {"what", "who", "where", "when", "why", "how", "which", "whose", "whom"}
17
-
18
-
19
- def is_question(text: str) -> bool:
20
- """Return True if the text is a question — questions are not stored as memories."""
21
- stripped = text.strip()
22
- if stripped.endswith("?"):
23
- return True
24
- first_word = re.split(r"\s+", stripped.lower())[0]
25
- return first_word in _QUESTION_WORDS
26
-
27
-
28
- def categorize(text: str) -> str:
29
- """
30
- Use spaCy dependency parse to classify:
31
- fact — declarative sentence with an explicit subject
32
- e.g. "Novak is world number 1", "I love Python", "My name is Sachit"
33
- assumption — imperative sentence with no subject (command/instruction)
34
- e.g. "Use python instead of JS", "Please convert this image"
35
- """
36
- doc = _nlp(text)
37
- has_subject = any(tok.dep_ in ("nsubj", "nsubjpass") for tok in doc)
38
- return "fact" if has_subject else "assumption"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes