etchmem 0.1.2__tar.gz → 0.1.3__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.3}/PKG-INFO +1 -1
  2. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem/consolidate/worker.py +22 -4
  3. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem/recall/recaller.py +12 -1
  4. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem.egg-info/PKG-INFO +1 -1
  5. {etchmem-0.1.2 → etchmem-0.1.3}/pyproject.toml +1 -1
  6. {etchmem-0.1.2 → etchmem-0.1.3}/LICENSE +0 -0
  7. {etchmem-0.1.2 → etchmem-0.1.3}/README.md +0 -0
  8. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem/__init__.py +0 -0
  9. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem/config.py +0 -0
  10. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem/connector/__init__.py +0 -0
  11. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem/connector/base.py +0 -0
  12. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem/connector/remote.py +0 -0
  13. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem/consolidate/__init__.py +0 -0
  14. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem/engine.py +0 -0
  15. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem/models.py +0 -0
  16. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem/recall/__init__.py +0 -0
  17. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem/stores/__init__.py +0 -0
  18. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem/stores/base.py +0 -0
  19. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem/stores/chroma_store.py +0 -0
  20. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem/util/__init__.py +0 -0
  21. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem/util/affectability.py +0 -0
  22. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem/util/hashing.py +0 -0
  23. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem.egg-info/SOURCES.txt +0 -0
  24. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem.egg-info/dependency_links.txt +0 -0
  25. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem.egg-info/requires.txt +0 -0
  26. {etchmem-0.1.2 → etchmem-0.1.3}/etchmem.egg-info/top_level.txt +0 -0
  27. {etchmem-0.1.2 → etchmem-0.1.3}/setup.cfg +0 -0
  28. {etchmem-0.1.2 → etchmem-0.1.3}/tests/test_consolidate_idempotent.py +0 -0
  29. {etchmem-0.1.2 → etchmem-0.1.3}/tests/test_formation_buckets.py +0 -0
  30. {etchmem-0.1.2 → etchmem-0.1.3}/tests/test_recall.py +0 -0
  31. {etchmem-0.1.2 → etchmem-0.1.3}/tests/test_reconsolidation.py +0 -0
  32. {etchmem-0.1.2 → etchmem-0.1.3}/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.3
4
4
  Summary: A pluggable, embeddable reconsolidating skill memory engine for AI agents
5
5
  Author: Andrey Olishchuk
6
6
  License: MIT
@@ -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.3
4
4
  Summary: A pluggable, embeddable reconsolidating skill memory engine for AI agents
5
5
  Author: Andrey Olishchuk
6
6
  License: MIT
@@ -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.3"
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
File without changes