yourmemory 1.4.75__tar.gz → 1.4.76__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 (60) hide show
  1. {yourmemory-1.4.75 → yourmemory-1.4.76}/PKG-INFO +1 -1
  2. {yourmemory-1.4.75 → yourmemory-1.4.76}/memory_mcp.py +0 -23
  3. {yourmemory-1.4.75 → yourmemory-1.4.76}/pyproject.toml +1 -1
  4. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/app.py +16 -19
  5. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/services/compaction.py +56 -0
  6. {yourmemory-1.4.75 → yourmemory-1.4.76}/yourmemory.egg-info/PKG-INFO +1 -1
  7. {yourmemory-1.4.75 → yourmemory-1.4.76}/LICENSE +0 -0
  8. {yourmemory-1.4.75 → yourmemory-1.4.76}/README.md +0 -0
  9. {yourmemory-1.4.75 → yourmemory-1.4.76}/setup.cfg +0 -0
  10. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/__init__.py +0 -0
  11. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/db/connection.py +0 -0
  12. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/db/duckdb_schema.sql +0 -0
  13. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/db/migrate.py +0 -0
  14. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/db/schema.sql +0 -0
  15. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/db/sqlite_schema.sql +0 -0
  16. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/graph/__init__.py +0 -0
  17. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/graph/backend.py +0 -0
  18. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/graph/graph_store.py +0 -0
  19. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/graph/neo4j_backend.py +0 -0
  20. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/graph/networkx_backend.py +0 -0
  21. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/graph/svo_extract.py +0 -0
  22. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/hook_templates/__init__.py +0 -0
  23. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/hook_templates/yourmemory_observe.py +0 -0
  24. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/hook_templates/yourmemory_recall.py +0 -0
  25. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/hook_templates/yourmemory_recall.sh +0 -0
  26. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/hook_templates/yourmemory_server.py +0 -0
  27. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/hook_templates/yourmemory_store.py +0 -0
  28. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/hook_templates/yourmemory_user.sh +0 -0
  29. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/jobs/decay_job.py +0 -0
  30. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/routes/__init__.py +0 -0
  31. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/routes/agents.py +0 -0
  32. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/routes/audit.py +0 -0
  33. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/routes/compact.py +0 -0
  34. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/routes/dsar.py +0 -0
  35. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/routes/graph_viz.py +0 -0
  36. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/routes/memories.py +0 -0
  37. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/routes/proxy.py +0 -0
  38. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/routes/retrieve.py +0 -0
  39. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/routes/ui.py +0 -0
  40. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/services/__init__.py +0 -0
  41. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/services/agent_registry.py +0 -0
  42. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/services/api_keys.py +0 -0
  43. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/services/audit.py +0 -0
  44. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/services/decay.py +0 -0
  45. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/services/embed.py +0 -0
  46. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/services/extract.py +0 -0
  47. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/services/extract_fallback.py +0 -0
  48. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/services/resolve.py +0 -0
  49. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/services/resolve_fallback.py +0 -0
  50. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/services/retrieve.py +0 -0
  51. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/services/session.py +0 -0
  52. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/services/temporal.py +0 -0
  53. {yourmemory-1.4.75 → yourmemory-1.4.76}/src/services/utils.py +0 -0
  54. {yourmemory-1.4.75 → yourmemory-1.4.76}/tests/test_features.py +0 -0
  55. {yourmemory-1.4.75 → yourmemory-1.4.76}/yourmemory.egg-info/SOURCES.txt +0 -0
  56. {yourmemory-1.4.75 → yourmemory-1.4.76}/yourmemory.egg-info/dependency_links.txt +0 -0
  57. {yourmemory-1.4.75 → yourmemory-1.4.76}/yourmemory.egg-info/entry_points.txt +0 -0
  58. {yourmemory-1.4.75 → yourmemory-1.4.76}/yourmemory.egg-info/requires.txt +0 -0
  59. {yourmemory-1.4.75 → yourmemory-1.4.76}/yourmemory.egg-info/top_level.txt +0 -0
  60. {yourmemory-1.4.75 → yourmemory-1.4.76}/yourmemory_run.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yourmemory
3
- Version: 1.4.75
3
+ Version: 1.4.76
4
4
  Summary: Persistent memory for Claude — Ebbinghaus forgetting curve, semantic deduplication, MCP-native
5
5
  Author: Sachit Misra
6
6
  Author-email: mishrasachit1@gmail.com
@@ -924,36 +924,13 @@ def _start_decay_scheduler():
924
924
  except Exception:
925
925
  pass
926
926
 
927
- def _compact():
928
- if os.getenv("YOURMEMORY_COMPACTION", "0") != "1":
929
- return
930
- try:
931
- from src.services.compaction import compact_user
932
- from src.db.connection import get_conn, get_backend
933
- b = get_backend(); conn = get_conn()
934
- try:
935
- if b == "duckdb":
936
- users = [r[0] for r in conn.execute("SELECT DISTINCT user_id FROM memories").fetchall()]
937
- else:
938
- cur = conn.cursor(); cur.execute("SELECT DISTINCT user_id FROM memories")
939
- users = [r[0] for r in cur.fetchall()]; cur.close()
940
- finally:
941
- conn.close()
942
- for u in users:
943
- try: compact_user(u)
944
- except Exception: pass
945
- except Exception:
946
- pass
947
-
948
927
  def loop():
949
928
  run_decay()
950
929
  _audit_prune()
951
- _compact()
952
930
  timer = threading.Event()
953
931
  while not timer.wait(timeout=86400):
954
932
  run_decay()
955
933
  _audit_prune()
956
- _compact()
957
934
 
958
935
  t = threading.Thread(target=loop, daemon=True, name="decay-scheduler")
959
936
  t.start()
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "yourmemory"
7
- version = "1.4.75"
7
+ version = "1.4.76"
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,25 +28,8 @@ def _daily_jobs():
28
28
  prune_audit() # retention: drop audit rows older than the (>=90-day) window
29
29
  except Exception:
30
30
  pass
31
- # Opt-in: compress clusters of related memories so the store stays lean over time.
32
- if os.getenv("YOURMEMORY_COMPACTION", "0") == "1":
33
- try:
34
- from src.services.compaction import compact_user
35
- from src.db.connection import get_conn, get_backend
36
- b = get_backend(); conn = get_conn()
37
- try:
38
- if b == "duckdb":
39
- users = [r[0] for r in conn.execute("SELECT DISTINCT user_id FROM memories").fetchall()]
40
- else:
41
- cur = conn.cursor(); cur.execute("SELECT DISTINCT user_id FROM memories")
42
- users = [r[0] for r in cur.fetchall()]; cur.close()
43
- finally:
44
- conn.close()
45
- for u in users:
46
- try: compact_user(u)
47
- except Exception: pass
48
- except Exception:
49
- pass
31
+ # Note: memory compaction is event-driven (triggered on store when a cluster reaches
32
+ # N), not a daily sweep — see /auto-store and src/services/compaction.py.
50
33
 
51
34
 
52
35
  @asynccontextmanager
@@ -677,4 +660,18 @@ def auto_store_endpoint(req: AutoStoreRequest):
677
660
  except Exception:
678
661
  pass
679
662
 
663
+ # Event-driven compaction: if a just-stored fact's cluster now has >= N closely
664
+ # related memories, compress that cluster immediately so the store stays lean —
665
+ # no daily sweep needed. On by default; disable with YOURMEMORY_COMPACTION=0.
666
+ if os.getenv("YOURMEMORY_COMPACTION", "1") == "1" and to_index:
667
+ try:
668
+ from src.services.compaction import maybe_compact_around
669
+ seen = set()
670
+ for _mid, content, _imp, _cat, _emb in to_index:
671
+ if content and content not in seen:
672
+ seen.add(content)
673
+ maybe_compact_around(user_id, content)
674
+ except Exception:
675
+ pass
676
+
680
677
  return {"stored": len(stored), "facts": stored}
@@ -151,6 +151,62 @@ def compact_user(user_id: str, min_cluster: int = None, sim_threshold: float = N
151
151
  "summaries": summaries, "scanned": len(mems)}
152
152
 
153
153
 
154
+ def _fetch_rows_by_ids(user_id: str, ids: list) -> list[dict]:
155
+ if not ids:
156
+ return []
157
+ backend = get_backend()
158
+ conn = get_conn()
159
+ cols = "id, content, category, importance, agent_id, visibility, created_at"
160
+ ph = ",".join(["%s" if backend == "postgres" else "?"] * len(ids))
161
+ sql = f"SELECT {cols} FROM memories WHERE user_id = {'%s' if backend=='postgres' else '?'} AND id IN ({ph})"
162
+ params = [user_id, *ids]
163
+ try:
164
+ if backend == "duckdb":
165
+ return duckdb_rows(conn.execute(sql, params))
166
+ cur = conn.cursor()
167
+ cur.execute(sql, tuple(params) if backend == "postgres" else params)
168
+ cn = [d[0] for d in cur.description]
169
+ rows = [dict(zip(cn, r)) for r in cur.fetchall()]; cur.close(); return rows
170
+ finally:
171
+ conn.close()
172
+
173
+
174
+ def maybe_compact_around(user_id: str, seed_content: str,
175
+ min_cluster: int = None, sim_threshold: float = None) -> int | None:
176
+ """Event-driven compaction: after a memory is stored, check whether its neighborhood
177
+ now has >= N closely-related memories; if so, compress just that cluster immediately.
178
+ Targeted (one similarity lookup, not an O(n^2) full scan). Returns the summary id."""
179
+ user_id = (user_id or "").strip().lower()
180
+ min_cluster = min_cluster or MIN_CLUSTER
181
+ sim = sim_threshold if sim_threshold is not None else SIM_THRESHOLD
182
+ if not seed_content or len(seed_content) < 4:
183
+ return None
184
+ try:
185
+ from src.services.retrieve import retrieve
186
+ res = retrieve(user_id, seed_content, top_k=max(min_cluster * 4, 20), no_graph=True)
187
+ except Exception:
188
+ return None
189
+ members = [m for m in res.get("memories", []) if m.get("similarity", 0) >= sim]
190
+ if len(members) < min_cluster:
191
+ return None # not enough of the same thing yet — leave it
192
+ rows = _fetch_rows_by_ids(user_id, [m["id"] for m in members])
193
+ if len(rows) < min_cluster:
194
+ return None
195
+ summary = _summarize([r["content"] for r in rows])
196
+ if not summary or len(summary) < 12:
197
+ return None
198
+ importance = max(float(r["importance"] or 0.5) for r in rows)
199
+ category = categorize(summary)
200
+ agent_id = rows[0].get("agent_id")
201
+ visibility = rows[0].get("visibility") or "shared"
202
+ summary_id = _apply_compaction(get_backend(), user_id, summary, importance, category,
203
+ agent_id, visibility, rows)
204
+ if summary_id is not None:
205
+ log_event("write", "compact", user_id,
206
+ detail={"clusters": 1, "archived": len(rows), "trigger": "count"})
207
+ return summary_id
208
+
209
+
154
210
  def _apply_compaction(backend, user_id, summary, importance, category, agent_id,
155
211
  visibility, members) -> int | None:
156
212
  """Insert the summary memory, archive the originals, delete them. Returns summary id."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yourmemory
3
- Version: 1.4.75
3
+ Version: 1.4.76
4
4
  Summary: Persistent memory for Claude — Ebbinghaus forgetting curve, semantic deduplication, MCP-native
5
5
  Author: Sachit Misra
6
6
  Author-email: mishrasachit1@gmail.com
File without changes
File without changes
File without changes
File without changes