luminary-memory 0.2.2__tar.gz → 0.2.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.
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/CHANGELOG.md +24 -1
- luminary_memory-0.2.4/PKG-INFO +176 -0
- luminary_memory-0.2.4/PLAN-v0.2.4.md +128 -0
- luminary_memory-0.2.4/README.md +142 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/docs/api.md +1 -1
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/docs/hermes-integration.md +1 -0
- luminary_memory-0.2.4/docs/lifecycle.md +108 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/hermes/README.md +19 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/hermes/SKILL.md +17 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/hermes/hooks/luminary-activity/handler.py +45 -18
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/hermes/install.sh +21 -15
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/pyproject.toml +1 -1
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/__init__.py +1 -1
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/api.py +155 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/cli.py +26 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/hermes/config.py +1 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/hermes/config_schema.py +2 -1
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/hermes/provider.py +7 -1
- luminary_memory-0.2.4/src/luminary_memory/ingest/llm.py +202 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/hermes/test_config.py +26 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/hermes/test_hooks.py +28 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/hermes/test_lifecycle.py +33 -0
- luminary_memory-0.2.4/tests/hermes/test_session_boundaries.py +128 -0
- luminary_memory-0.2.4/tests/hermes/test_sync_turn.py +137 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/hermes/test_tools.py +34 -0
- luminary_memory-0.2.4/tests/test_api.py +184 -0
- luminary_memory-0.2.4/tests/test_api_extended.py +371 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_cli.py +49 -0
- luminary_memory-0.2.4/tests/test_ingest_llm.py +177 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/website/index.html +24 -14
- luminary_memory-0.2.2/PKG-INFO +0 -271
- luminary_memory-0.2.2/README.md +0 -237
- luminary_memory-0.2.2/docs/lifecycle.md +0 -31
- luminary_memory-0.2.2/src/luminary_memory/ingest/llm.py +0 -149
- luminary_memory-0.2.2/tests/hermes/test_session_boundaries.py +0 -67
- luminary_memory-0.2.2/tests/hermes/test_sync_turn.py +0 -73
- luminary_memory-0.2.2/tests/test_api.py +0 -68
- luminary_memory-0.2.2/tests/test_api_extended.py +0 -83
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/.github/workflows/ci.yml +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/.github/workflows/pages.yml +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/.github/workflows/publish.yml +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/.gitignore +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/CODE_OF_CONDUCT.md +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/CONTRIBUTING.md +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/LICENSE +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/PLAN-v0.2.0.md +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/PLAN-v0.2.1.md +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/PLAN.md +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/ROADMAP.md +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/SECURITY.md +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/benchmarks/README.md +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/benchmarks/RESULTS.md +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/benchmarks/__init__.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/benchmarks/hermes_provider_bench.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/benchmarks/metrics.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/benchmarks/run_benchmarks.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/benchmarks/synthetic.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/docs/architecture.md +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/docs/backends.md +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/docs/cli.md +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/docs/index.md +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/docs/quickstart.md +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/docs/recall.md +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/hermes/hooks/luminary-activity/HOOK.yaml +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/hermes/hooks/luminary-activity/README.md +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/backends/__init__.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/backends/base.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/backends/pgvector.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/backends/sqlite.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/budget.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/config.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/embeddings/__init__.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/embeddings/fastembed.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/export.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/hermes/__init__.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/hermes/plugin.yaml +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/ingest/__init__.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/ingest/whitelist.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/lifecycle/__init__.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/lifecycle/cleanup.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/lifecycle/consolidate.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/lifecycle/prune.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/lifecycle/runner.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/recall/__init__.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/recall/dedup.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/recall/fusion.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/recall/graph.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/recall/keyword.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/recall/planner.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/recall/semantic.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/recall/snippets.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/recall/temporal.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/schema.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/src/luminary_memory/types.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/__init__.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/conftest.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/hermes/test_backup_paths.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/hermes/test_entrypoint.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/hermes/test_provider_import.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/hermes/test_recall.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/hermes/test_system_prompt.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/hermes_stubs/agent/memory_provider.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_backend_pgvector.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_backend_sqlite.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_budget.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_dedup_jaccard.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_embeddings.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_fusion_rrf.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_ingest_whitelist.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_lifecycle.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_recall_graph.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_recall_keyword.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_recall_orchestrator.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_recall_semantic.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_recall_temporal.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_schema.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_t10_export.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_t11_limit_zero.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_t1_hnsw.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_t3_planner.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_t4_batch.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_t5_enricher.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_t6_tag_scoped.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_t7_updated_at.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_t8_snippets.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/tests/test_t9_cli_json.py +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/website/.nojekyll +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/website/css/luminary.css +0 -0
- {luminary_memory-0.2.2 → luminary_memory-0.2.4}/website/js/luminary.js +0 -0
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented in this file. The format follows [Keep a Changelog](https://keepachangelog.com/), and the project adheres to [Semantic Versioning](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## [0.2.4] - 2026-08-18
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **`MemoryClient.health_score()`** — store health report (0-100) across five dimensions: duplicate rate, staleness, importance, graph density, and size — plus actionable recommendations. Empty store scores 100.
|
|
10
|
+
- **`luminary-memory health` CLI** — human-readable bar + `--json` output.
|
|
11
|
+
- **Test coverage raised 82% → 90%** (roadmap DoD): config_schema standalone shim, enricher `_call_llm`/`review_memories` paths, maintenance CRUD edges, provider lifecycle/maintenance/shutdown, CLI whitelist/limit errors, recall strategy failure fallbacks.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **`run_maintenance` bad-response detection** — `data.get("actions") or []` masked non-list responses as "no actions"; now correctly reports `{"error": "bad LLM response"}`.
|
|
16
|
+
|
|
17
|
+
## [0.2.3] - 2026-08-18
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **Hermes dashboard config schema** — exposes 18 luminary settings (mode, backend, recall limit, token budget, auto-recall/retain, **LLM memory curation**, **auto-maintain**, LLM endpoint, indicators) in the Hermes dashboard Provider panel. `auto_maintain` added; `ingest_llm` label corrected to "LLM memory curation".
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- **Balanced LLM curation** — the enricher now rejects work/task logs and meta-talk about the memory system itself, while still keeping durable facts (not over-strict). Verified: work logs → `worth_saving: false`; facts → stored.
|
|
26
|
+
|
|
5
27
|
## [0.2.2] - 2026-08-18
|
|
6
28
|
|
|
7
29
|
### Added
|
|
@@ -10,7 +32,8 @@ All notable changes to this project are documented in this file. The format foll
|
|
|
10
32
|
- `worth_saving` — chit-chat, greetings, and trivial turns are **dropped** instead of polluting the store.
|
|
11
33
|
- `summary` — kept turns are stored as concise factual summaries (e.g. `"Deploy target is the staging cluster."`) instead of raw `User: ... / Assistant: ...` transcripts.
|
|
12
34
|
- `entities` / `tags` — attached for richer recall.
|
|
13
|
-
- **
|
|
35
|
+
- **LLM store maintenance** — `MemoryClient.run_maintenance()` + provider `auto_maintain` (session-end): the LLM reviews the store and **deletes obsolete/contradicted/duplicate facts**, **updates changed ones**, and **keeps** current ones (verified: stale deploy target auto-removed).
|
|
36
|
+
- **Transparency log** — provider writes `$HERMES_HOME/luminary/luminary.log` recording initialize/recall/retain/maintenance/errors.
|
|
14
37
|
- **`OpenAICompatibleEnricher` User-Agent header** — required by some OpenAI-compatible gateways (e.g. Command Code 403s without it).
|
|
15
38
|
|
|
16
39
|
### Fixed
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: luminary-memory
|
|
3
|
+
Version: 0.2.4
|
|
4
|
+
Summary: A lightweight, self-hosted memory layer for AI agents
|
|
5
|
+
Project-URL: Homepage, https://github.com/alertxsto/luminary-memory
|
|
6
|
+
Project-URL: Repository, https://github.com/alertxsto/luminary-memory
|
|
7
|
+
Project-URL: Documentation, https://github.com/alertxsto/luminary-memory#readme
|
|
8
|
+
Author: Dwiky Candra
|
|
9
|
+
License: Apache-2.0
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: fastembed>=0.4.0
|
|
21
|
+
Requires-Dist: numpy>=1.26
|
|
22
|
+
Requires-Dist: pgvector>=0.2.5
|
|
23
|
+
Requires-Dist: psycopg[binary]>=3.1
|
|
24
|
+
Requires-Dist: rich>=13.0
|
|
25
|
+
Requires-Dist: typer>=0.12
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: build>=1.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: pdoc>=14; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
32
|
+
Provides-Extra: hermes
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# 🌙 luminary-memory
|
|
36
|
+
|
|
37
|
+
**A lightweight, self-hosted memory layer for AI agents.**
|
|
38
|
+
|
|
39
|
+
[](https://pypi.org/project/luminary-memory)
|
|
40
|
+
[](https://pypi.org/project/luminary-memory)
|
|
41
|
+
[](LICENSE)
|
|
42
|
+
[](https://github.com/alertxsto/luminary-memory/actions)
|
|
43
|
+
[](https://github.com/alertxsto/luminary-memory/actions)
|
|
44
|
+
[](https://github.com/alertxsto/luminary-memory)
|
|
45
|
+
[](https://github.com/alertxsto/luminary-memory)
|
|
46
|
+
|
|
47
|
+
**Self-hosted · Private · Budget-aware · Self-maintaining**
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## What your agent remembers is what it becomes.
|
|
52
|
+
|
|
53
|
+
Agents are only as good as what they remember. A stateless agent re-learns the same context every session — paying the same tokens, making the same mistakes. luminary-memory closes that gap with a local memory store that persists between runs, retrieves the right context on demand, and keeps itself tidy over time.
|
|
54
|
+
|
|
55
|
+
**Four retrieval strategies. One fused answer. Zero cloud.**
|
|
56
|
+
|
|
57
|
+
- **Semantic** — ONNX embeddings (384-dim, CPU, no GPU needed)
|
|
58
|
+
- **Keyword** — FTS5 BM25 (SQLite, zero config)
|
|
59
|
+
- **Temporal** — recency decay × access count
|
|
60
|
+
- **Graph** — entity co-occurrence with automatic curation
|
|
61
|
+
|
|
62
|
+
Strategies run in parallel and fuse via **Reciprocal Rank Fusion (k=60)** → **Jaccard deduplication (0.85)** → **token budget (4096)**.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Quickstart
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install luminary-memory
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from luminary_memory import MemoryClient
|
|
74
|
+
|
|
75
|
+
client = MemoryClient(db_path="memory.db")
|
|
76
|
+
|
|
77
|
+
# store a durable fact
|
|
78
|
+
client.ingest("The deploy target is the staging cluster", tags=["deploy", "infra"])
|
|
79
|
+
|
|
80
|
+
# recall — four strategies fused into one ranked answer
|
|
81
|
+
result = client.recall("where do we deploy?")
|
|
82
|
+
for memory, score in zip(result.memories, result.scores):
|
|
83
|
+
print(f"{score:.3f} {memory.content}")
|
|
84
|
+
# → 0.942 The deploy target is the staging cluster
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# CLI
|
|
89
|
+
luminary-memory add "deploy target is staging" --tags deploy
|
|
90
|
+
luminary-memory recall "where do we deploy?" --json
|
|
91
|
+
luminary-memory list
|
|
92
|
+
luminary-memory lifecycle
|
|
93
|
+
luminary-memory stats
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Hermes Agent — first-class memory provider
|
|
99
|
+
|
|
100
|
+
Drop-in. Install the provider with `pip install "luminary-memory[hermes]"`, then add
|
|
101
|
+
`memory.provider: luminary` to your Hermes config. That's it.
|
|
102
|
+
|
|
103
|
+
From the next session: **auto-recall** injects relevant memories every turn,
|
|
104
|
+
**auto-save** persists completed turns, and the model can call
|
|
105
|
+
`luminary_recall` / `luminary_ingest` / `luminary_list` on demand.
|
|
106
|
+
|
|
107
|
+
Two optional LLM-powered features keep the store sharp:
|
|
108
|
+
|
|
109
|
+
- **`ingest_llm`** — evaluates every turn before saving: drops chit-chat, stores factual summaries instead of raw transcripts.
|
|
110
|
+
- **`auto_maintain`** — reviews the store at session end: keeps current facts, updates changed ones, deletes stale or duplicate ones.
|
|
111
|
+
|
|
112
|
+
18 settings are exposed in the [Hermes dashboard](https://alertxsto.github.io/luminary-memory) for zero-hassle tuning. See
|
|
113
|
+
[hermes/README.md](hermes/README.md) for the one-shot installer and full configuration.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Configuration
|
|
118
|
+
|
|
119
|
+
Every setting has a `LUMINARY_*` env var or a `Settings` object.
|
|
120
|
+
|
|
121
|
+
| Setting | Env var | Default |
|
|
122
|
+
|---------|---------|---------|
|
|
123
|
+
| `backend` | `LUMINARY_BACKEND` | `sqlite` |
|
|
124
|
+
| `db_path` | `LUMINARY_DB_PATH` | `luminary_memory.db` |
|
|
125
|
+
| `pg_dsn` | `LUMINARY_PG_DSN` | — (pgvector only) |
|
|
126
|
+
| `embedding_model` | `LUMINARY_EMBEDDING_MODEL` | `BAAI/bge-small-en-v1.5` |
|
|
127
|
+
| `embedding_dim` | `LUMINARY_EMBEDDING_DIM` | `384` |
|
|
128
|
+
| `ingest_llm` | `LUMINARY_INGEST_LLM` | `false` |
|
|
129
|
+
| `rrf_k` | `LUMINARY_RRF_K` | `60` |
|
|
130
|
+
| `dedup_jaccard_threshold` | `LUMINARY_DEDUP_JACCARD_THRESHOLD` | `0.85` |
|
|
131
|
+
| `token_budget` | `LUMINARY_TOKEN_BUDGET` | `4096` |
|
|
132
|
+
| `ttl_default_seconds` | `LUMINARY_TTL_DEFAULT_SECONDS` | `null` |
|
|
133
|
+
| `prune_min_importance` | `LUMINARY_PRUNE_MIN_IMPORTANCE` | `0.2` |
|
|
134
|
+
| `consolidate_jaccard_threshold` | `LUMINARY_CONSOLIDATE_JACCARD_THRESHOLD` | `0.9` |
|
|
135
|
+
|
|
136
|
+
See [hermes/SKILL.md](hermes/SKILL.md) for the full provider config table (18 settings).
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Architecture
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
ingest(text) ──► whitelist filter ──► (LLM curation) ──► embed ──► SQLite / pgvector
|
|
144
|
+
|
|
145
|
+
recall(query) ──► semantic │ keyword │ temporal │ graph
|
|
146
|
+
──► RRF fusion ──► Jaccard dedup ──► token budget ──► ranked results
|
|
147
|
+
|
|
148
|
+
lifecycle() ──► cleanup (TTL) ──► consolidate (near-dupes) ──► prune (low-value)
|
|
149
|
+
|
|
150
|
+
maintenance() ──► LLM reviews store ──► keep │ update │ delete stale facts
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Built-in lifecycle keeps the store lean. LLM maintenance (optional) keeps it accurate. `health_score()` gives you a 0-100 checkup with actionable recommendations.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Documentation
|
|
158
|
+
|
|
159
|
+
| Section | |
|
|
160
|
+
|---------|-----|
|
|
161
|
+
| [Quickstart](docs/quickstart.md) | Install and first use |
|
|
162
|
+
| [Architecture](docs/architecture.md) | Pipelines and data flow |
|
|
163
|
+
| [Python API](docs/api.md) | `MemoryClient` reference |
|
|
164
|
+
| [CLI](docs/cli.md) | All subcommands |
|
|
165
|
+
| [Recall](docs/recall.md) | Four strategies + fusion |
|
|
166
|
+
| [Lifecycle](docs/lifecycle.md) | Cleanup, consolidation, pruning, LLM maintenance |
|
|
167
|
+
| [Backends](docs/backends.md) | SQLite vs pgvector |
|
|
168
|
+
| [Hermes integration](docs/hermes-integration.md) | Provider, config, installer |
|
|
169
|
+
| [Roadmap](ROADMAP.md) | v0.2.3 → v1.0.0 |
|
|
170
|
+
| [Benchmarks](benchmarks/RESULTS.md) | 230 ms recall @ 5k, 0 LLM tokens |
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
[Apache-2.0](LICENSE) © 2026 Dwiky Candra
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# PLAN v0.2.4 — Coverage 90% + Health Score
|
|
2
|
+
|
|
3
|
+
**Version:** 0.2.4 · **Milestone:** Quality hardening + health introspection
|
|
4
|
+
**Goal:** Raise test coverage from 82% → ≥90% (roadmap DoD) and ship
|
|
5
|
+
`MemoryClient.health_score()` for store quality monitoring.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Phase 1 — Coverage: test the untested (T1–T5)
|
|
10
|
+
|
|
11
|
+
Targets identified by `pytest --cov --cov-report=term-missing`:
|
|
12
|
+
|
|
13
|
+
| Module | Current | Target | Missing lines to cover |
|
|
14
|
+
|--------|---------|--------|------------------------|
|
|
15
|
+
| `hermes/config_schema.py` | 11% | ≥90% | Standalone fallback shim (no hermes runtime) |
|
|
16
|
+
| `ingest/llm.py` | 68% | ≥90% | `review_memories`, `_call_llm` error/empty paths, enrich failures |
|
|
17
|
+
| `api.py` | 75% | ≥90% | `run_maintenance` actions, `delete`/`update` edges, error handling |
|
|
18
|
+
| `hermes/provider.py` | 81% | ≥90% | maintenance trigger, shutdown, prefetch guards, error paths |
|
|
19
|
+
|
|
20
|
+
### T1 — config_schema standalone coverage
|
|
21
|
+
- Test: import `config_schema.py` **without** hermes-agent runtime (simulate CI)
|
|
22
|
+
- Assert `CONFIG_SCHEMA` shim exposes `name`, `label`, `storage`, 18 fields
|
|
23
|
+
- Assert `auto_maintain` and `ingest_llm` fields present
|
|
24
|
+
|
|
25
|
+
### T2 — llm.py enricher paths
|
|
26
|
+
- Test `OpenAICompatibleEnricher.review_memories` with mocked `_call_llm`
|
|
27
|
+
- returns valid actions JSON → parsed
|
|
28
|
+
- returns `{}` → `{"skipped": ...}` handled by caller
|
|
29
|
+
- raises (network) → returns `{}` (best-effort, no crash)
|
|
30
|
+
- Test `_call_llm` with mocked `urlopen`:
|
|
31
|
+
- 200 with choices → content returned
|
|
32
|
+
- 200 empty choices → `""`
|
|
33
|
+
- HTTPError → propagates to caller fallback
|
|
34
|
+
- Test `enrich()` fallback when `base_url` empty → `EnrichedContent(content=text)`
|
|
35
|
+
|
|
36
|
+
### T3 — api.py maintenance + CRUD edges
|
|
37
|
+
- Test `run_maintenance`:
|
|
38
|
+
- no enricher → `{"skipped": ...}`
|
|
39
|
+
- empty store → `{"reviewed": 0, ...}`
|
|
40
|
+
- enricher returns delete/update/keep actions → applied correctly
|
|
41
|
+
- enricher returns malformed actions (not a list) → `{"error": "bad LLM response"}`
|
|
42
|
+
- enricher returns action for unknown id → skipped
|
|
43
|
+
- Test `update()` with changed content → `updated_at` bumped, embedding recomputed
|
|
44
|
+
- Test `delete()` on non-existent id → graceful (no raise)
|
|
45
|
+
|
|
46
|
+
### T4 — provider.py lifecycle paths
|
|
47
|
+
- Test `on_session_end` with `auto_maintain=true` + `ingest_llm=true` → `run_maintenance` called, logged
|
|
48
|
+
- Test `on_session_end` with maintenance raising → exception logged, not propagated
|
|
49
|
+
- Test `shutdown` writer-thread close (regression: no SQLite thread-affinity crash)
|
|
50
|
+
- Test prefetch guards: `auto_recall=false`, `mode=tools`, shutdown flag → no prefetch
|
|
51
|
+
|
|
52
|
+
### T5 — coverage gate
|
|
53
|
+
- Add `--cov-fail-under=90` to CI workflow + `Makefile`/`pyproject` pytest config
|
|
54
|
+
- Verify `pytest -q --cov=luminary_memory` ≥ 90% locally
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Phase 2 — Health score (T6–T8)
|
|
59
|
+
|
|
60
|
+
### T6 — `health_score()` API in `api.py`
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
def health_score(self) -> dict:
|
|
64
|
+
"""Store health report: overall 0-100 + per-dimension breakdown."""
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Dimensions (computed from existing store data — no new schema):
|
|
68
|
+
|
|
69
|
+
| Dimension | Weight | Signal |
|
|
70
|
+
|-----------|--------|--------|
|
|
71
|
+
| `duplicate_rate` | 25% | % memories with a near-dupe (Jaccard > 0.85) — lower better |
|
|
72
|
+
| `staleness` | 25% | % memories not accessed in 30d (decayed) — lower better |
|
|
73
|
+
| `importance` | 20% | % memories above `prune_min_importance` — higher better |
|
|
74
|
+
| `density` | 15% | memories with graph relations / total — higher better |
|
|
75
|
+
| `size` | 15% | store size vs `token_budget` scale (0 = empty, 100 = healthy volume) |
|
|
76
|
+
|
|
77
|
+
Score = weighted sum. Returns:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
{
|
|
81
|
+
"score": 87.5,
|
|
82
|
+
"dimensions": {
|
|
83
|
+
"duplicate_rate": {"value": 0.02, "weight": 0.25, "health": 98.0},
|
|
84
|
+
"staleness": {...}, "importance": {...}, "density": {...}, "size": {...}
|
|
85
|
+
},
|
|
86
|
+
"recommendations": ["2 stale memories older than 30d — run lifecycle prune"]
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`recommendations` — generated from low-scoring dimensions (≤70):
|
|
91
|
+
- duplicates → "run lifecycle consolidate"
|
|
92
|
+
- stale → "run lifecycle prune or LLM maintenance"
|
|
93
|
+
- low importance → "raise prune_min_importance or review store"
|
|
94
|
+
|
|
95
|
+
### T7 — CLI `health` subcommand
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
luminary-memory health --json
|
|
99
|
+
luminary-memory health
|
|
100
|
+
# 📊 Memory Health: 87.5/100
|
|
101
|
+
# • Duplicates: 2.0% ✅
|
|
102
|
+
# • Stale (>30d): 4.0% ✅
|
|
103
|
+
# • Importance: 92% ✅
|
|
104
|
+
# • Graph density: 33% ⚠️
|
|
105
|
+
# • Size: 60% ✅
|
|
106
|
+
# → 2 stale memories — run `luminary-memory lifecycle`
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### T8 — health score tests + docs
|
|
110
|
+
|
|
111
|
+
- Test: empty store → score 0? or 100? (decide: empty store = 100 "nothing wrong" — document)
|
|
112
|
+
- Test: all duplicates → low duplicate_rate dimension
|
|
113
|
+
- Test: all stale → low staleness dimension
|
|
114
|
+
- Test: healthy mixed store → score ≥ 80
|
|
115
|
+
- Test CLI `health` output shape (`--json` parseable)
|
|
116
|
+
- Docs: `docs/lifecycle.md` + README (health section), CHANGELOG entry
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Definition of Done (v0.2.4)
|
|
121
|
+
|
|
122
|
+
- [ ] `pytest -q` green (≥90% coverage, CI gate)
|
|
123
|
+
- [ ] `ruff check src tests` clean
|
|
124
|
+
- [ ] CLI smoke: `luminary-memory health` + `--json`
|
|
125
|
+
- [ ] Health score verified against real store (`~/.hermes/luminary/memory.db`)
|
|
126
|
+
- [ ] CHANGELOG 0.2.4 entry
|
|
127
|
+
- [ ] Version bumped to 0.2.4
|
|
128
|
+
- [ ] Tag + GitHub release + PyPI upload (auto-publish)
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# 🌙 luminary-memory
|
|
2
|
+
|
|
3
|
+
**A lightweight, self-hosted memory layer for AI agents.**
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/luminary-memory)
|
|
6
|
+
[](https://pypi.org/project/luminary-memory)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](https://github.com/alertxsto/luminary-memory/actions)
|
|
9
|
+
[](https://github.com/alertxsto/luminary-memory/actions)
|
|
10
|
+
[](https://github.com/alertxsto/luminary-memory)
|
|
11
|
+
[](https://github.com/alertxsto/luminary-memory)
|
|
12
|
+
|
|
13
|
+
**Self-hosted · Private · Budget-aware · Self-maintaining**
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## What your agent remembers is what it becomes.
|
|
18
|
+
|
|
19
|
+
Agents are only as good as what they remember. A stateless agent re-learns the same context every session — paying the same tokens, making the same mistakes. luminary-memory closes that gap with a local memory store that persists between runs, retrieves the right context on demand, and keeps itself tidy over time.
|
|
20
|
+
|
|
21
|
+
**Four retrieval strategies. One fused answer. Zero cloud.**
|
|
22
|
+
|
|
23
|
+
- **Semantic** — ONNX embeddings (384-dim, CPU, no GPU needed)
|
|
24
|
+
- **Keyword** — FTS5 BM25 (SQLite, zero config)
|
|
25
|
+
- **Temporal** — recency decay × access count
|
|
26
|
+
- **Graph** — entity co-occurrence with automatic curation
|
|
27
|
+
|
|
28
|
+
Strategies run in parallel and fuse via **Reciprocal Rank Fusion (k=60)** → **Jaccard deduplication (0.85)** → **token budget (4096)**.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Quickstart
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install luminary-memory
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from luminary_memory import MemoryClient
|
|
40
|
+
|
|
41
|
+
client = MemoryClient(db_path="memory.db")
|
|
42
|
+
|
|
43
|
+
# store a durable fact
|
|
44
|
+
client.ingest("The deploy target is the staging cluster", tags=["deploy", "infra"])
|
|
45
|
+
|
|
46
|
+
# recall — four strategies fused into one ranked answer
|
|
47
|
+
result = client.recall("where do we deploy?")
|
|
48
|
+
for memory, score in zip(result.memories, result.scores):
|
|
49
|
+
print(f"{score:.3f} {memory.content}")
|
|
50
|
+
# → 0.942 The deploy target is the staging cluster
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# CLI
|
|
55
|
+
luminary-memory add "deploy target is staging" --tags deploy
|
|
56
|
+
luminary-memory recall "where do we deploy?" --json
|
|
57
|
+
luminary-memory list
|
|
58
|
+
luminary-memory lifecycle
|
|
59
|
+
luminary-memory stats
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Hermes Agent — first-class memory provider
|
|
65
|
+
|
|
66
|
+
Drop-in. Install the provider with `pip install "luminary-memory[hermes]"`, then add
|
|
67
|
+
`memory.provider: luminary` to your Hermes config. That's it.
|
|
68
|
+
|
|
69
|
+
From the next session: **auto-recall** injects relevant memories every turn,
|
|
70
|
+
**auto-save** persists completed turns, and the model can call
|
|
71
|
+
`luminary_recall` / `luminary_ingest` / `luminary_list` on demand.
|
|
72
|
+
|
|
73
|
+
Two optional LLM-powered features keep the store sharp:
|
|
74
|
+
|
|
75
|
+
- **`ingest_llm`** — evaluates every turn before saving: drops chit-chat, stores factual summaries instead of raw transcripts.
|
|
76
|
+
- **`auto_maintain`** — reviews the store at session end: keeps current facts, updates changed ones, deletes stale or duplicate ones.
|
|
77
|
+
|
|
78
|
+
18 settings are exposed in the [Hermes dashboard](https://alertxsto.github.io/luminary-memory) for zero-hassle tuning. See
|
|
79
|
+
[hermes/README.md](hermes/README.md) for the one-shot installer and full configuration.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Configuration
|
|
84
|
+
|
|
85
|
+
Every setting has a `LUMINARY_*` env var or a `Settings` object.
|
|
86
|
+
|
|
87
|
+
| Setting | Env var | Default |
|
|
88
|
+
|---------|---------|---------|
|
|
89
|
+
| `backend` | `LUMINARY_BACKEND` | `sqlite` |
|
|
90
|
+
| `db_path` | `LUMINARY_DB_PATH` | `luminary_memory.db` |
|
|
91
|
+
| `pg_dsn` | `LUMINARY_PG_DSN` | — (pgvector only) |
|
|
92
|
+
| `embedding_model` | `LUMINARY_EMBEDDING_MODEL` | `BAAI/bge-small-en-v1.5` |
|
|
93
|
+
| `embedding_dim` | `LUMINARY_EMBEDDING_DIM` | `384` |
|
|
94
|
+
| `ingest_llm` | `LUMINARY_INGEST_LLM` | `false` |
|
|
95
|
+
| `rrf_k` | `LUMINARY_RRF_K` | `60` |
|
|
96
|
+
| `dedup_jaccard_threshold` | `LUMINARY_DEDUP_JACCARD_THRESHOLD` | `0.85` |
|
|
97
|
+
| `token_budget` | `LUMINARY_TOKEN_BUDGET` | `4096` |
|
|
98
|
+
| `ttl_default_seconds` | `LUMINARY_TTL_DEFAULT_SECONDS` | `null` |
|
|
99
|
+
| `prune_min_importance` | `LUMINARY_PRUNE_MIN_IMPORTANCE` | `0.2` |
|
|
100
|
+
| `consolidate_jaccard_threshold` | `LUMINARY_CONSOLIDATE_JACCARD_THRESHOLD` | `0.9` |
|
|
101
|
+
|
|
102
|
+
See [hermes/SKILL.md](hermes/SKILL.md) for the full provider config table (18 settings).
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Architecture
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
ingest(text) ──► whitelist filter ──► (LLM curation) ──► embed ──► SQLite / pgvector
|
|
110
|
+
|
|
111
|
+
recall(query) ──► semantic │ keyword │ temporal │ graph
|
|
112
|
+
──► RRF fusion ──► Jaccard dedup ──► token budget ──► ranked results
|
|
113
|
+
|
|
114
|
+
lifecycle() ──► cleanup (TTL) ──► consolidate (near-dupes) ──► prune (low-value)
|
|
115
|
+
|
|
116
|
+
maintenance() ──► LLM reviews store ──► keep │ update │ delete stale facts
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Built-in lifecycle keeps the store lean. LLM maintenance (optional) keeps it accurate. `health_score()` gives you a 0-100 checkup with actionable recommendations.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Documentation
|
|
124
|
+
|
|
125
|
+
| Section | |
|
|
126
|
+
|---------|-----|
|
|
127
|
+
| [Quickstart](docs/quickstart.md) | Install and first use |
|
|
128
|
+
| [Architecture](docs/architecture.md) | Pipelines and data flow |
|
|
129
|
+
| [Python API](docs/api.md) | `MemoryClient` reference |
|
|
130
|
+
| [CLI](docs/cli.md) | All subcommands |
|
|
131
|
+
| [Recall](docs/recall.md) | Four strategies + fusion |
|
|
132
|
+
| [Lifecycle](docs/lifecycle.md) | Cleanup, consolidation, pruning, LLM maintenance |
|
|
133
|
+
| [Backends](docs/backends.md) | SQLite vs pgvector |
|
|
134
|
+
| [Hermes integration](docs/hermes-integration.md) | Provider, config, installer |
|
|
135
|
+
| [Roadmap](ROADMAP.md) | v0.2.3 → v1.0.0 |
|
|
136
|
+
| [Benchmarks](benchmarks/RESULTS.md) | 230 ms recall @ 5k, 0 LLM tokens |
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
[Apache-2.0](LICENSE) © 2026 Dwiky Candra
|
|
@@ -14,7 +14,7 @@ pdoc --output-dir docs/api --docformat markdown src/luminary_memory
|
|
|
14
14
|
|
|
15
15
|
## Quick surface
|
|
16
16
|
|
|
17
|
-
- `MemoryClient` — `ingest`, `ingest_batch`, `recall` (tags + snippets + planner), `search`, `get`/`update`/`delete`, `list`, `export`/`import_memories`, `run_lifecycle`, `stats`, `close`.
|
|
17
|
+
- `MemoryClient` — `ingest`, `ingest_batch`, `recall` (tags + snippets + planner), `search`, `get`/`update`/`delete`, `list`, `export`/`import_memories`, `run_lifecycle`, `run_maintenance` (LLM curation), `stats`, `close`.
|
|
18
18
|
- `Settings` — `LUMINARY_*` env vars, `as_dict()`.
|
|
19
19
|
- `Memory`, `RecallResult` — `types.py`.
|
|
20
20
|
- Subpackages `backends/`, `embeddings/`, `ingest/`, `recall/`, `lifecycle/`, `export`.
|
|
@@ -49,6 +49,7 @@ The provider reads `$HERMES_HOME/luminary/config.json` (created on first save wi
|
|
|
49
49
|
| `llm_timeout` | `60` | Enricher request timeout (seconds) |
|
|
50
50
|
| `recall_indicator` | `true` | Show `🌙 Luminary — recalled N memories` |
|
|
51
51
|
| `retain_indicator` | `true` | Show `🌙 Luminary — memory saved` |
|
|
52
|
+
| `auto_maintain` | `false` | **LLM store review at session end** — keeps/updates/deletes stale, contradicted, or duplicate facts (requires `ingest_llm`) |
|
|
52
53
|
|
|
53
54
|
### LLM memory curation (v0.2.2+)
|
|
54
55
|
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Lifecycle
|
|
2
|
+
|
|
3
|
+
Two maintenance layers keep the store lean: deterministic passes
|
|
4
|
+
(`run_lifecycle()`) and LLM-driven curation (`run_maintenance()`).
|
|
5
|
+
|
|
6
|
+
## `run_lifecycle()` — deterministic passes
|
|
7
|
+
|
|
8
|
+
### cleanup — TTL expiry
|
|
9
|
+
|
|
10
|
+
Removes memories whose `ttl_seconds` has elapsed.
|
|
11
|
+
|
|
12
|
+
### consolidate — merge near-duplicates
|
|
13
|
+
|
|
14
|
+
Clusters memories by Jaccard similarity (`LUMINARY_CONSOLIDATE_JACCARD_THRESHOLD`, default 0.9) and merges each cluster into a single memory — keeping the longest content, summing access counts, and unioning tags.
|
|
15
|
+
|
|
16
|
+
### prune — drop low-value memories
|
|
17
|
+
|
|
18
|
+
Removes memories below a minimum importance (`LUMINARY_PRUNE_MIN_IMPORTANCE`, default 0.2).
|
|
19
|
+
|
|
20
|
+
## `run_maintenance()` — LLM-driven curation (v0.2.2+)
|
|
21
|
+
|
|
22
|
+
Sends the store to the configured LLM enricher, which reviews every memory and
|
|
23
|
+
decides **keep** / **update** / **delete**:
|
|
24
|
+
|
|
25
|
+
- **delete** — obsolete, contradicted, or duplicate facts (e.g. the old
|
|
26
|
+
deploy target after it changed).
|
|
27
|
+
- **update** — facts that changed, with replacement content.
|
|
28
|
+
- **keep** — still-current facts.
|
|
29
|
+
|
|
30
|
+
Requires an LLM enricher (set `ingest_llm: true` + `llm_*` config); no-ops
|
|
31
|
+
otherwise. One LLM call per review run (all memories in a single call).
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
client = MemoryClient(db_path="memory.db", enricher=enricher)
|
|
35
|
+
print(client.run_maintenance())
|
|
36
|
+
# {'reviewed': 4, 'deleted': 2, 'updated': 0}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Automatic maintenance in the Hermes provider
|
|
40
|
+
|
|
41
|
+
With `auto_maintain: true` in `~/.hermes/luminary/config.json` (plus
|
|
42
|
+
`ingest_llm: true`), the provider runs `run_maintenance()` automatically at
|
|
43
|
+
every session end:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"ingest_llm": true,
|
|
48
|
+
"llm_base_url": "https://api.commandcode.ai/provider/v1",
|
|
49
|
+
"llm_model": "deepseek/deepseek-v4-flash",
|
|
50
|
+
"llm_api_key": "your-key",
|
|
51
|
+
"auto_maintain": true
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Results are recorded in the transparency log
|
|
56
|
+
(`~/.hermes/luminary/luminary.log`): `maintenance {'reviewed': N, 'deleted': N, 'updated': N}`.
|
|
57
|
+
|
|
58
|
+
## `health_score()` — store health report (v0.2.4+)
|
|
59
|
+
|
|
60
|
+
Returns a 0-100 health score with a per-dimension breakdown, computed from
|
|
61
|
+
existing store data (no new schema):
|
|
62
|
+
|
|
63
|
+
| Dimension | Weight | What it measures |
|
|
64
|
+
|-----------|--------|------------------|
|
|
65
|
+
| `duplicate_rate` | 25% | Share of memories with a near-duplicate (Jaccard > threshold) |
|
|
66
|
+
| `staleness` | 25% | Share of memories not accessed in 30 days |
|
|
67
|
+
| `importance` | 20% | Share of memories above `prune_min_importance` |
|
|
68
|
+
| `density` | 15% | Share of memories with graph relations |
|
|
69
|
+
| `size` | 15% | Store volume (scales toward 100 at ~1k memories) |
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
report = client.health_score()
|
|
73
|
+
print(report["score"]) # 87.5
|
|
74
|
+
print(report["recommendations"]) # actionable hints
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
CLI:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
luminary-memory health # human-readable bar
|
|
81
|
+
luminary-memory health --json # raw JSON
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
📊 Memory Health: 87.5/100 █████████░
|
|
86
|
+
✅ duplicate_rate: 98%
|
|
87
|
+
⚠️ density: 33%
|
|
88
|
+
→ 2 stale memories (>30d) — run lifecycle prune or LLM maintenance
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Empty store scores 100 (nothing wrong); low-scoring dimensions produce
|
|
92
|
+
recommendations.
|
|
93
|
+
|
|
94
|
+
## Scheduling
|
|
95
|
+
|
|
96
|
+
Run via cron for a self-maintaining store:
|
|
97
|
+
|
|
98
|
+
```cron
|
|
99
|
+
0 4 * * * /usr/local/bin/luminary-memory lifecycle
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Or programmatically:
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
client = MemoryClient(db_path="memory.db")
|
|
106
|
+
print(client.run_lifecycle())
|
|
107
|
+
client.close()
|
|
108
|
+
```
|
|
@@ -61,6 +61,25 @@ Any OpenAI-compatible endpoint works. Costs one small LLM call per retained
|
|
|
61
61
|
turn (temperature 0, strict JSON). If the enricher fails, the provider falls
|
|
62
62
|
back to storing the turn verbatim — it never blocks.
|
|
63
63
|
|
|
64
|
+
### Store maintenance (auto_maintain)
|
|
65
|
+
|
|
66
|
+
With `auto_maintain: true` (plus `ingest_llm: true`), the provider also
|
|
67
|
+
reviews the store at every session end — the LLM keeps current facts, updates
|
|
68
|
+
changed ones, and **deletes stale, contradicted, or duplicate memories**:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"ingest_llm": true,
|
|
73
|
+
"llm_base_url": "https://api.commandcode.ai/provider/v1",
|
|
74
|
+
"llm_model": "deepseek/deepseek-v4-flash",
|
|
75
|
+
"llm_api_key": "your-key",
|
|
76
|
+
"auto_maintain": true
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Results land in the transparency log:
|
|
81
|
+
`maintenance {'reviewed': N, 'deleted': N, 'updated': N}`.
|
|
82
|
+
|
|
64
83
|
## Manual install (no script)
|
|
65
84
|
|
|
66
85
|
```bash
|
|
@@ -76,6 +76,22 @@ client.list(limit=20) # most recent first
|
|
|
76
76
|
Use this before re-ingesting — if a fact is already stored, update instead of
|
|
77
77
|
duplicating.
|
|
78
78
|
|
|
79
|
+
### Store maintenance
|
|
80
|
+
|
|
81
|
+
The store keeps itself clean two ways:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
# deterministic passes (TTL, consolidate, prune)
|
|
85
|
+
client.run_lifecycle()
|
|
86
|
+
|
|
87
|
+
# LLM review: keep current facts, update changed ones, delete stale/duplicates
|
|
88
|
+
client.run_maintenance() # requires an LLM enricher (ingest_llm)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
In the Hermes provider, `auto_maintain: true` (plus `ingest_llm: true`) runs
|
|
92
|
+
`run_maintenance()` automatically at every session end — no manual trigger
|
|
93
|
+
needed. Results are logged to `~/.hermes/luminary/luminary.log`.
|
|
94
|
+
|
|
79
95
|
## Configuration — Tweaks
|
|
80
96
|
|
|
81
97
|
Provider config lives in `~/.hermes/luminary/config.json` (auto-created,
|
|
@@ -92,6 +108,7 @@ Provider config lives in `~/.hermes/luminary/config.json` (auto-created,
|
|
|
92
108
|
| `retain_every_n_turns` | `1` | Save every N turns (higher = fewer, batched saves) |
|
|
93
109
|
| `ingest_llm` | `false` | **LLM memory curation** — drops chit-chat, stores factual summary instead of raw transcript |
|
|
94
110
|
| `llm_base_url` / `llm_model` / `llm_api_key` | `""` | OpenAI-compatible enricher endpoint/model/key |
|
|
111
|
+
| `auto_maintain` | `false` | **LLM store review at session end** — keeps/updates/deletes stale or duplicate facts (needs `ingest_llm`) |
|
|
95
112
|
| `recall_indicator` | `true` | Show `🌙 Luminary — recalled N memories` |
|
|
96
113
|
| `retain_indicator` | `true` | Show `🌙 Luminary — memory saved` |
|
|
97
114
|
| `backend` | `sqlite` | `sqlite` (zero config) or `pgvector` (scale) |
|