etchmem 0.1.2__tar.gz → 0.1.4__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 (32) hide show
  1. {etchmem-0.1.2 → etchmem-0.1.4}/PKG-INFO +10 -4
  2. {etchmem-0.1.2 → etchmem-0.1.4}/README.md +9 -3
  3. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem/consolidate/worker.py +22 -4
  4. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem/recall/recaller.py +12 -1
  5. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem.egg-info/PKG-INFO +10 -4
  6. {etchmem-0.1.2 → etchmem-0.1.4}/pyproject.toml +1 -1
  7. {etchmem-0.1.2 → etchmem-0.1.4}/LICENSE +0 -0
  8. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem/__init__.py +0 -0
  9. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem/config.py +0 -0
  10. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem/connector/__init__.py +0 -0
  11. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem/connector/base.py +0 -0
  12. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem/connector/remote.py +0 -0
  13. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem/consolidate/__init__.py +0 -0
  14. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem/engine.py +0 -0
  15. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem/models.py +0 -0
  16. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem/recall/__init__.py +0 -0
  17. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem/stores/__init__.py +0 -0
  18. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem/stores/base.py +0 -0
  19. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem/stores/chroma_store.py +0 -0
  20. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem/util/__init__.py +0 -0
  21. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem/util/affectability.py +0 -0
  22. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem/util/hashing.py +0 -0
  23. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem.egg-info/SOURCES.txt +0 -0
  24. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem.egg-info/dependency_links.txt +0 -0
  25. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem.egg-info/requires.txt +0 -0
  26. {etchmem-0.1.2 → etchmem-0.1.4}/etchmem.egg-info/top_level.txt +0 -0
  27. {etchmem-0.1.2 → etchmem-0.1.4}/setup.cfg +0 -0
  28. {etchmem-0.1.2 → etchmem-0.1.4}/tests/test_consolidate_idempotent.py +0 -0
  29. {etchmem-0.1.2 → etchmem-0.1.4}/tests/test_formation_buckets.py +0 -0
  30. {etchmem-0.1.2 → etchmem-0.1.4}/tests/test_recall.py +0 -0
  31. {etchmem-0.1.2 → etchmem-0.1.4}/tests/test_reconsolidation.py +0 -0
  32. {etchmem-0.1.2 → etchmem-0.1.4}/tests/test_remember_recall.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: etchmem
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: A pluggable, embeddable reconsolidating skill memory engine for AI agents
5
5
  Author: Andrey Olishchuk
6
6
  License: MIT
@@ -28,7 +28,13 @@ Dynamic: license-file
28
28
 
29
29
  # etchmem
30
30
 
31
- A lightweight, embeddable memory engine for LLM agents. Agents remember things, recall them later, and the stored knowledge quietly matures through use — no extra plumbing required.
31
+ **Where agents grow up — not where they take notes.**
32
+
33
+ Modern AI agents are built from skills. Every time a skill runs, it leaves a trail: results, feedback, corrections, surprises. That trail is experience — and experience is how an agent learns how the world actually works.
34
+
35
+ `etchmem` is memory for that maturation. Not a scratchpad for the current session. Not a user profile to personalize replies. Not a long chat log you grep when context runs out. It is the layer where raw observations turn into durable understanding — how things work, what users react to, what failed last time — and keep sharpening every time the agent draws on it again.
36
+
37
+ Over time, knowledge compounds. The agent stops merely following scenarios and starts carrying a worldview shaped by use.
32
38
 
33
39
  ```bash
34
40
  pip install etchmem
@@ -38,7 +44,7 @@ pip install etchmem
38
44
 
39
45
  ## What it is
40
46
 
41
- `etchmem` gives your agent a persistent memory that improves over time. You deposit raw observations, retrieve them with natural-language queries, and periodically consolidate — at which point the engine synthesizes scattered signals into compact knowledge articles and rewrites anything that's been recalled against fresh context.
47
+ `etchmem` gives your agent a persistent memory that matures through living. You deposit raw observations — facts, outcomes, feedback — scoped to a skill or left general. You retrieve with natural-language queries. Periodically you consolidate, and the engine synthesizes scattered signal into compact knowledge articles, rewriting anything that has been recalled against fresh context.
42
48
 
43
49
  It is not a vector database and not an agent runtime. It sits above [ChromaDB](https://www.trychroma.com/), which handles all storage and embedding, and exposes exactly **three methods**.
44
50
 
@@ -52,7 +58,7 @@ Memory is kept in three tiers:
52
58
  - **Buffer** — a working space for deposits and recall-events, waiting for the next consolidation run.
53
59
  - **Injected** — synthesized knowledge articles, the primary search target. Only current knowledge is kept; superseded articles are hard-deleted.
54
60
 
55
- The key idea is **reconsolidation**: every `recall()` call emits a recall-event into the buffer. When you later call `consolidate()`, the engine detects which injected articles were used, checks whether fresh signal changes anything, and rewrites them if needed. Knowledge that gets recalled stays current as a side-effect of being recalled. Knowledge nobody asks about simply rests.
61
+ The key idea is **reconsolidation**: every `recall()` call emits a recall-event into the buffer. When you later call `consolidate()`, the engine detects which injected articles were used, checks whether fresh signal changes anything, and rewrites them if needed. Knowledge that gets recalled stays current as a side-effect of being recalled. Knowledge nobody asks about simply rests. This is how a skill — or the agent as a whole — accumulates experience instead of starting from zero each run.
56
62
 
57
63
  Embeddings are computed locally by ChromaDB's built-in model — no external embedding API. Synthesis (the rewrite step inside `consolidate`) calls an LLM via `OPENAI_API_KEY` or `ANTHROPIC_API_KEY`.
58
64
 
@@ -1,6 +1,12 @@
1
1
  # etchmem
2
2
 
3
- A lightweight, embeddable memory engine for LLM agents. Agents remember things, recall them later, and the stored knowledge quietly matures through use — no extra plumbing required.
3
+ **Where agents grow up — not where they take notes.**
4
+
5
+ Modern AI agents are built from skills. Every time a skill runs, it leaves a trail: results, feedback, corrections, surprises. That trail is experience — and experience is how an agent learns how the world actually works.
6
+
7
+ `etchmem` is memory for that maturation. Not a scratchpad for the current session. Not a user profile to personalize replies. Not a long chat log you grep when context runs out. It is the layer where raw observations turn into durable understanding — how things work, what users react to, what failed last time — and keep sharpening every time the agent draws on it again.
8
+
9
+ Over time, knowledge compounds. The agent stops merely following scenarios and starts carrying a worldview shaped by use.
4
10
 
5
11
  ```bash
6
12
  pip install etchmem
@@ -10,7 +16,7 @@ pip install etchmem
10
16
 
11
17
  ## What it is
12
18
 
13
- `etchmem` gives your agent a persistent memory that improves over time. You deposit raw observations, retrieve them with natural-language queries, and periodically consolidate — at which point the engine synthesizes scattered signals into compact knowledge articles and rewrites anything that's been recalled against fresh context.
19
+ `etchmem` gives your agent a persistent memory that matures through living. You deposit raw observations — facts, outcomes, feedback — scoped to a skill or left general. You retrieve with natural-language queries. Periodically you consolidate, and the engine synthesizes scattered signal into compact knowledge articles, rewriting anything that has been recalled against fresh context.
14
20
 
15
21
  It is not a vector database and not an agent runtime. It sits above [ChromaDB](https://www.trychroma.com/), which handles all storage and embedding, and exposes exactly **three methods**.
16
22
 
@@ -24,7 +30,7 @@ Memory is kept in three tiers:
24
30
  - **Buffer** — a working space for deposits and recall-events, waiting for the next consolidation run.
25
31
  - **Injected** — synthesized knowledge articles, the primary search target. Only current knowledge is kept; superseded articles are hard-deleted.
26
32
 
27
- The key idea is **reconsolidation**: every `recall()` call emits a recall-event into the buffer. When you later call `consolidate()`, the engine detects which injected articles were used, checks whether fresh signal changes anything, and rewrites them if needed. Knowledge that gets recalled stays current as a side-effect of being recalled. Knowledge nobody asks about simply rests.
33
+ The key idea is **reconsolidation**: every `recall()` call emits a recall-event into the buffer. When you later call `consolidate()`, the engine detects which injected articles were used, checks whether fresh signal changes anything, and rewrites them if needed. Knowledge that gets recalled stays current as a side-effect of being recalled. Knowledge nobody asks about simply rests. This is how a skill — or the agent as a whole — accumulates experience instead of starting from zero each run.
28
34
 
29
35
  Embeddings are computed locally by ChromaDB's built-in model — no external embedding API. Synthesis (the rewrite step inside `consolidate`) calls an LLM via `OPENAI_API_KEY` or `ANTHROPIC_API_KEY`.
30
36
 
@@ -25,6 +25,7 @@ from __future__ import annotations
25
25
  import json
26
26
  import time
27
27
  from dataclasses import dataclass, field
28
+ from datetime import datetime, timezone
28
29
  from typing import Any
29
30
 
30
31
  from etchmem.config import Config
@@ -53,6 +54,9 @@ accurate knowledge article from a set of raw signal fragments.
53
54
  ## Instructions
54
55
  - Write a single, cohesive knowledge article that captures all key facts.
55
56
  - Do NOT invent facts not present in the fragments.
57
+ - Each fragment may be prefixed with a timestamp like `[21 Jun 2025, 15:25 UTC]`.
58
+ Preserve these timestamps verbatim inline, anchoring the facts they precede,
59
+ e.g. "As of 21 Jun 2025, 15:25 UTC, the deployment was rolled back."
56
60
  - At the end of the article, output a JSON block on its own line:
57
61
  {{"tags": {{"access_level": "...", "topic": "...", "product": "..."}}}}
58
62
 
@@ -74,6 +78,9 @@ accurate and incorporates the new information.
74
78
  - Preserve ALL load-bearing facts from the existing article.
75
79
  - Incorporate relevant new information from the fresh context.
76
80
  - Remove outdated claims only when the fresh context explicitly supersedes them.
81
+ - Fresh context fragments may be prefixed with a timestamp like `[21 Jun 2025, 15:25 UTC]`.
82
+ Preserve these timestamps verbatim inline, anchoring the facts they precede.
83
+ Also carry forward any timestamps already present in the existing article.
77
84
  - At the end, output a JSON block on its own line:
78
85
  {{"tags": {{"access_level": "...", "topic": "...", "product": "..."}}}}
79
86
 
@@ -93,6 +100,13 @@ Reply with exactly one word: YES or NO.
93
100
  """
94
101
 
95
102
 
103
+ def _stamp(ts: float) -> str:
104
+ """Format a Unix timestamp as a human-readable UTC string, e.g. '21 Jun 2025, 15:25 UTC'."""
105
+ if not ts:
106
+ return "unknown time"
107
+ return datetime.fromtimestamp(ts, tz=timezone.utc).strftime("%-d %b %Y, %H:%M UTC")
108
+
109
+
96
110
  def _parse_synthesis_output(raw: str) -> tuple[str, dict]:
97
111
  """Split LLM output into (article_text, tags_dict)."""
98
112
  lines = raw.strip().split("\n")
@@ -351,8 +365,10 @@ class Worker:
351
365
  return [item]
352
366
  return []
353
367
 
354
- # Synthesize
355
- fragments = "\n\n---\n\n".join(d.content for d in cluster.deposits)
368
+ # Synthesize — prefix each fragment with its absolute UTC timestamp
369
+ fragments = "\n\n---\n\n".join(
370
+ f"[{_stamp(d.created_at)}]\n{d.content}" for d in cluster.deposits
371
+ )
356
372
  prompt = _FORMATION_PROMPT.format(fragments=fragments)
357
373
  raw = self._connector.complete(prompt)
358
374
  article_text, tags = _parse_synthesis_output(raw)
@@ -396,8 +412,10 @@ class Worker:
396
412
  current_articles[r.id] = item
397
413
  break
398
414
 
399
- # Fresh deposit content for the cluster
400
- fresh_texts = [d.content for d in cluster.deposits]
415
+ # Fresh deposit content for the cluster — prefix with absolute UTC timestamps
416
+ fresh_texts = [
417
+ f"[{_stamp(d.created_at)}]\n{d.content}" for d in cluster.deposits
418
+ ]
401
419
  snapshots = [re.relational_snapshot for re in cluster.recall_events]
402
420
  all_fresh = fresh_texts + snapshots
403
421
  fresh_combined = "\n\n---\n\n".join(t for t in all_fresh if t.strip())
@@ -18,6 +18,7 @@ from __future__ import annotations
18
18
 
19
19
  import time
20
20
  import uuid
21
+ from datetime import datetime, timezone
21
22
  from typing import Any
22
23
 
23
24
  from etchmem.config import Config
@@ -87,13 +88,23 @@ class Recaller:
87
88
  merged = self._merge(injected_results, relational_results, top_k)
88
89
 
89
90
  # ── 4. Emit recall-event into buffer ──────────────────────────────
90
- relational_snapshot = "\n\n".join(r.content for r in relational_results)
91
+ # Stamp each relational hit so time context survives into the snapshot
92
+ relational_snapshot = "\n\n".join(
93
+ f"[{self._stamp(r.created_at)}]\n{r.content}" for r in relational_results
94
+ )
91
95
  self._emit_recall_event(query, relational_snapshot, skill, hint)
92
96
 
93
97
  return merged
94
98
 
95
99
  # ── Helpers ───────────────────────────────────────────────────────────
96
100
 
101
+ @staticmethod
102
+ def _stamp(ts: float) -> str:
103
+ """Format a Unix timestamp as a human-readable UTC string."""
104
+ if not ts:
105
+ return "unknown time"
106
+ return datetime.fromtimestamp(ts, tz=timezone.utc).strftime("%-d %b %Y, %H:%M UTC")
107
+
97
108
  def _merge(
98
109
  self,
99
110
  injected: list[SearchResult],
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: etchmem
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: A pluggable, embeddable reconsolidating skill memory engine for AI agents
5
5
  Author: Andrey Olishchuk
6
6
  License: MIT
@@ -28,7 +28,13 @@ Dynamic: license-file
28
28
 
29
29
  # etchmem
30
30
 
31
- A lightweight, embeddable memory engine for LLM agents. Agents remember things, recall them later, and the stored knowledge quietly matures through use — no extra plumbing required.
31
+ **Where agents grow up — not where they take notes.**
32
+
33
+ Modern AI agents are built from skills. Every time a skill runs, it leaves a trail: results, feedback, corrections, surprises. That trail is experience — and experience is how an agent learns how the world actually works.
34
+
35
+ `etchmem` is memory for that maturation. Not a scratchpad for the current session. Not a user profile to personalize replies. Not a long chat log you grep when context runs out. It is the layer where raw observations turn into durable understanding — how things work, what users react to, what failed last time — and keep sharpening every time the agent draws on it again.
36
+
37
+ Over time, knowledge compounds. The agent stops merely following scenarios and starts carrying a worldview shaped by use.
32
38
 
33
39
  ```bash
34
40
  pip install etchmem
@@ -38,7 +44,7 @@ pip install etchmem
38
44
 
39
45
  ## What it is
40
46
 
41
- `etchmem` gives your agent a persistent memory that improves over time. You deposit raw observations, retrieve them with natural-language queries, and periodically consolidate — at which point the engine synthesizes scattered signals into compact knowledge articles and rewrites anything that's been recalled against fresh context.
47
+ `etchmem` gives your agent a persistent memory that matures through living. You deposit raw observations — facts, outcomes, feedback — scoped to a skill or left general. You retrieve with natural-language queries. Periodically you consolidate, and the engine synthesizes scattered signal into compact knowledge articles, rewriting anything that has been recalled against fresh context.
42
48
 
43
49
  It is not a vector database and not an agent runtime. It sits above [ChromaDB](https://www.trychroma.com/), which handles all storage and embedding, and exposes exactly **three methods**.
44
50
 
@@ -52,7 +58,7 @@ Memory is kept in three tiers:
52
58
  - **Buffer** — a working space for deposits and recall-events, waiting for the next consolidation run.
53
59
  - **Injected** — synthesized knowledge articles, the primary search target. Only current knowledge is kept; superseded articles are hard-deleted.
54
60
 
55
- The key idea is **reconsolidation**: every `recall()` call emits a recall-event into the buffer. When you later call `consolidate()`, the engine detects which injected articles were used, checks whether fresh signal changes anything, and rewrites them if needed. Knowledge that gets recalled stays current as a side-effect of being recalled. Knowledge nobody asks about simply rests.
61
+ The key idea is **reconsolidation**: every `recall()` call emits a recall-event into the buffer. When you later call `consolidate()`, the engine detects which injected articles were used, checks whether fresh signal changes anything, and rewrites them if needed. Knowledge that gets recalled stays current as a side-effect of being recalled. Knowledge nobody asks about simply rests. This is how a skill — or the agent as a whole — accumulates experience instead of starting from zero each run.
56
62
 
57
63
  Embeddings are computed locally by ChromaDB's built-in model — no external embedding API. Synthesis (the rewrite step inside `consolidate`) calls an LLM via `OPENAI_API_KEY` or `ANTHROPIC_API_KEY`.
58
64
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "etchmem"
7
- version = "0.1.2"
7
+ version = "0.1.4"
8
8
  description = "A pluggable, embeddable reconsolidating skill memory engine for AI agents"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes