luminary-memory 0.2.4__tar.gz → 0.2.6__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.4 → luminary_memory-0.2.6}/.github/workflows/publish.yml +3 -3
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/CHANGELOG.md +28 -0
- luminary_memory-0.2.6/CONTRIBUTING.md +81 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/PKG-INFO +6 -4
- luminary_memory-0.2.6/PLAN-v0.2.5.md +120 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/README.md +5 -3
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/ROADMAP.md +5 -5
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/docs/api.md +1 -1
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/docs/architecture.md +6 -2
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/docs/cli.md +33 -1
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/docs/hermes-integration.md +2 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/docs/index.md +2 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/docs/lifecycle.md +16 -1
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/docs/quickstart.md +39 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/hermes/SKILL.md +5 -1
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/pyproject.toml +1 -1
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/__init__.py +1 -1
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/api.py +18 -10
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/backends/pgvector.py +20 -3
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/backends/sqlite.py +4 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/cli.py +12 -2
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/config.py +9 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/hermes/config_schema.py +2 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/hermes/provider.py +4 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/ingest/llm.py +12 -6
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/lifecycle/cleanup.py +4 -0
- luminary_memory-0.2.6/src/luminary_memory/lifecycle/consolidate.py +87 -0
- luminary_memory-0.2.6/src/luminary_memory/lifecycle/importance.py +68 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/lifecycle/prune.py +4 -0
- luminary_memory-0.2.6/src/luminary_memory/lifecycle/runner.py +52 -0
- luminary_memory-0.2.6/src/luminary_memory/recall/dedup.py +50 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_api_extended.py +21 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_cli.py +11 -1
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_dedup_jaccard.py +47 -0
- luminary_memory-0.2.6/tests/test_importance.py +145 -0
- luminary_memory-0.2.6/tests/test_lifecycle.py +189 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/website/index.html +1 -1
- luminary_memory-0.2.4/CONTRIBUTING.md +0 -45
- luminary_memory-0.2.4/src/luminary_memory/lifecycle/consolidate.py +0 -47
- luminary_memory-0.2.4/src/luminary_memory/lifecycle/runner.py +0 -26
- luminary_memory-0.2.4/src/luminary_memory/recall/dedup.py +0 -30
- luminary_memory-0.2.4/tests/test_lifecycle.py +0 -92
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/.github/workflows/ci.yml +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/.github/workflows/pages.yml +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/.gitignore +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/CODE_OF_CONDUCT.md +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/LICENSE +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/PLAN-v0.2.0.md +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/PLAN-v0.2.1.md +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/PLAN-v0.2.4.md +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/PLAN.md +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/SECURITY.md +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/benchmarks/README.md +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/benchmarks/RESULTS.md +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/benchmarks/__init__.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/benchmarks/hermes_provider_bench.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/benchmarks/metrics.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/benchmarks/run_benchmarks.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/benchmarks/synthetic.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/docs/backends.md +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/docs/recall.md +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/hermes/README.md +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/hermes/hooks/luminary-activity/HOOK.yaml +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/hermes/hooks/luminary-activity/README.md +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/hermes/hooks/luminary-activity/handler.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/hermes/install.sh +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/backends/__init__.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/backends/base.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/budget.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/embeddings/__init__.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/embeddings/fastembed.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/export.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/hermes/__init__.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/hermes/config.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/hermes/plugin.yaml +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/ingest/__init__.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/ingest/whitelist.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/lifecycle/__init__.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/recall/__init__.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/recall/fusion.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/recall/graph.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/recall/keyword.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/recall/planner.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/recall/semantic.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/recall/snippets.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/recall/temporal.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/schema.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/src/luminary_memory/types.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/__init__.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/conftest.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/hermes/test_backup_paths.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/hermes/test_config.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/hermes/test_entrypoint.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/hermes/test_hooks.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/hermes/test_lifecycle.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/hermes/test_provider_import.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/hermes/test_recall.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/hermes/test_session_boundaries.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/hermes/test_sync_turn.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/hermes/test_system_prompt.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/hermes/test_tools.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/hermes_stubs/agent/memory_provider.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_api.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_backend_pgvector.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_backend_sqlite.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_budget.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_embeddings.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_fusion_rrf.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_ingest_llm.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_ingest_whitelist.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_recall_graph.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_recall_keyword.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_recall_orchestrator.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_recall_semantic.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_recall_temporal.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_schema.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_t10_export.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_t11_limit_zero.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_t1_hnsw.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_t3_planner.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_t4_batch.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_t5_enricher.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_t6_tag_scoped.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_t7_updated_at.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_t8_snippets.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/tests/test_t9_cli_json.py +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/website/.nojekyll +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/website/css/luminary.css +0 -0
- {luminary_memory-0.2.4 → luminary_memory-0.2.6}/website/js/luminary.js +0 -0
|
@@ -35,9 +35,9 @@ jobs:
|
|
|
35
35
|
ls -lh skill_asset/luminary-memory-skill.zip
|
|
36
36
|
- name: Publish to PyPI (trusted publishing / OIDC)
|
|
37
37
|
uses: pypa/gh-action-pypi-publish@release/v1
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
38
|
+
# Trusted Publisher only — no API token. Configured at
|
|
39
|
+
# https://pypi.org/manage/account/publishing/. Never fall back to twine:
|
|
40
|
+
# an API token upload alongside Trusted Publishing triggers a PyPI alert.
|
|
41
41
|
- name: Attach skill asset to GitHub Release
|
|
42
42
|
uses: softprops/action-gh-release@v2
|
|
43
43
|
with:
|
|
@@ -2,6 +2,34 @@
|
|
|
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.6] - 2026-08-18
|
|
6
|
+
|
|
7
|
+
### Polish & hardening
|
|
8
|
+
|
|
9
|
+
- **Lifecycle logging** — `cleanup`, `consolidate` (mode + merged count), `prune`, and `run_lifecycle` (result + duration) now log their activity; `estimate_importance` logs via the runner.
|
|
10
|
+
- **SQLite batch errors logged** — `add_many` failures log the exception before rollback/re-raise (was silent).
|
|
11
|
+
- **CLI recall JSON richer** — each memory now includes `source`, `created_at`, and `importance`.
|
|
12
|
+
- **Env var warnings** — invalid `LUMINARY_*` int/float values warn instead of silently falling back.
|
|
13
|
+
- **Dashboard schema 20 fields** — `consolidate_semantic` (Semantic consolidation) and `importance_auto` (Auto importance estimation) added to the Hermes provider config.
|
|
14
|
+
- **`recall(limit=0)` truly unlimited** — passes `None` to backends instead of a 10k magic cap.
|
|
15
|
+
- **Dedup capped window** — `dedup_jaccard(max_pairs=500)` keeps recall O(n·k) instead of O(n²) on large result sets.
|
|
16
|
+
- **`import_memories` error logging** — failures log the path before re-raise.
|
|
17
|
+
- **pgvector JSON columns safe** — `metadata`/`tags` parse via a `_json_load` helper (corrupt data falls back instead of crashing); HNSW index failure now logged.
|
|
18
|
+
- **Prefetch thread join** — `queue_prefetch` joins any in-flight worker before spawning a new one (no thread leak / stale-cache overwrite).
|
|
19
|
+
- **Degenerate embedding guard** — semantic consolidation falls back to Jaccard when embeddings are all-equal (no false merges of unrelated memories).
|
|
20
|
+
- **`CONTRIBUTING.md`** — new Testing section (Postgres/pgvector setup, coverage ≥ 90%) pointing at [issue #1](https://github.com/alertxsto/luminary-memory/issues/1) (add Postgres to CI).
|
|
21
|
+
|
|
22
|
+
## [0.2.5] - 2026-08-18
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- **Semantic consolidation** — `consolidate(semantic=True)` merges memories by embedding-cosine similarity (default `LUMINARY_CONSOLIDATE_SEMANTIC=true`), catching paraphrases that token-overlap misses; falls back to Jaccard when embeddings are missing. CLI: `luminary-memory lifecycle --semantic/--no-semantic`.
|
|
27
|
+
- **Auto importance estimation** — `lifecycle/importance.py:estimate_importance()` scores each memory from access (`log1p`), recency (24h decay), and graph centrality; applied on ingest and re-estimated before every prune (`LUMINARY_IMPORTANCE_AUTO=true`). Pruning and the health score's importance dimension now use live values.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- `run_lifecycle()` now also re-estimates importances (returns `reestimated` count).
|
|
32
|
+
|
|
5
33
|
## [0.2.4] - 2026-08-18
|
|
6
34
|
|
|
7
35
|
### Added
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for helping build luminary-memory.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone <repo> && cd luminary-memory
|
|
9
|
+
pip install -e ".[dev]"
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Development workflow
|
|
13
|
+
|
|
14
|
+
1. Create a branch: `git checkout -b feat/your-change`.
|
|
15
|
+
2. Write a failing test first (`tests/`).
|
|
16
|
+
3. Implement the minimal change.
|
|
17
|
+
4. Run the checks:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
python -m pytest
|
|
21
|
+
python -m ruff check src tests
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
5. Commit with a conventional message.
|
|
25
|
+
|
|
26
|
+
## Commit conventions
|
|
27
|
+
|
|
28
|
+
- `feat:` — new feature
|
|
29
|
+
- `fix:` — bug fix
|
|
30
|
+
- `docs:` — documentation only
|
|
31
|
+
- `improve:` — refactor or enhancement without behavior change
|
|
32
|
+
- `chore:` — build/tooling
|
|
33
|
+
|
|
34
|
+
## Coding standards
|
|
35
|
+
|
|
36
|
+
- Python 3.11+, type-hinted.
|
|
37
|
+
- `ruff` clean, `pytest` green.
|
|
38
|
+
- TDD where feasible — test first, then implement.
|
|
39
|
+
- Keep the public API (`MemoryClient`) stable; add rather than break.
|
|
40
|
+
|
|
41
|
+
## Pull requests
|
|
42
|
+
|
|
43
|
+
- One logical change per PR.
|
|
44
|
+
- Link the issue if applicable.
|
|
45
|
+
- CI must pass before merge.
|
|
46
|
+
|
|
47
|
+
## Testing
|
|
48
|
+
|
|
49
|
+
Run the full suite (SQLite + unit tests) locally:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
python -m pytest # ~200+ tests, green without any external service
|
|
53
|
+
python -m ruff check src tests
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Coverage (must stay ≥ 90%):
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
python -m pytest --cov=luminary_memory --cov-report=term
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Postgres / pgvector integration tests
|
|
63
|
+
|
|
64
|
+
`tests/test_backend_pgvector.py` contains real-Postgres round-trips that are
|
|
65
|
+
**skipped unless `LUMINARY_PG_DSN` is set** — CI currently does not provide
|
|
66
|
+
Postgres, so the pgvector path is the least-covered module (59%). If you're
|
|
67
|
+
working on the pgvector backend, run them locally with:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# spin up a throwaway Postgres with the pgvector extension
|
|
71
|
+
docker run -d --name luminary-pg -p 5432:5432 \
|
|
72
|
+
-e POSTGRES_USER=luminary -e POSTGRES_PASSWORD=luminary -e POSTGRES_DB=luminary \
|
|
73
|
+
pgvector/pgvector:pg16
|
|
74
|
+
|
|
75
|
+
# point the tests at it
|
|
76
|
+
export LUMINARY_PG_DSN=postgresql://luminary:luminary@localhost:5432/luminary
|
|
77
|
+
python -m pytest tests/test_backend_pgvector.py -v
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
See [issue #1](https://github.com/alertxsto/luminary-memory/issues/1) for
|
|
81
|
+
adding a Postgres service to CI so these tests run automatically.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: luminary-memory
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.6
|
|
4
4
|
Summary: A lightweight, self-hosted memory layer for AI agents
|
|
5
5
|
Project-URL: Homepage, https://github.com/alertxsto/luminary-memory
|
|
6
6
|
Project-URL: Repository, https://github.com/alertxsto/luminary-memory
|
|
@@ -40,8 +40,8 @@ Description-Content-Type: text/markdown
|
|
|
40
40
|
[](https://pypi.org/project/luminary-memory)
|
|
41
41
|
[](LICENSE)
|
|
42
42
|
[](https://github.com/alertxsto/luminary-memory/actions)
|
|
43
|
-
[](https://github.com/alertxsto/luminary-memory/actions)
|
|
44
|
+
[](https://github.com/alertxsto/luminary-memory)
|
|
45
45
|
[](https://github.com/alertxsto/luminary-memory)
|
|
46
46
|
|
|
47
47
|
**Self-hosted · Private · Budget-aware · Self-maintaining**
|
|
@@ -132,6 +132,8 @@ Every setting has a `LUMINARY_*` env var or a `Settings` object.
|
|
|
132
132
|
| `ttl_default_seconds` | `LUMINARY_TTL_DEFAULT_SECONDS` | `null` |
|
|
133
133
|
| `prune_min_importance` | `LUMINARY_PRUNE_MIN_IMPORTANCE` | `0.2` |
|
|
134
134
|
| `consolidate_jaccard_threshold` | `LUMINARY_CONSOLIDATE_JACCARD_THRESHOLD` | `0.9` |
|
|
135
|
+
| `consolidate_semantic` | `LUMINARY_CONSOLIDATE_SEMANTIC` | `true` |
|
|
136
|
+
| `importance_auto` | `LUMINARY_IMPORTANCE_AUTO` | `true` |
|
|
135
137
|
|
|
136
138
|
See [hermes/SKILL.md](hermes/SKILL.md) for the full provider config table (18 settings).
|
|
137
139
|
|
|
@@ -166,7 +168,7 @@ Built-in lifecycle keeps the store lean. LLM maintenance (optional) keeps it acc
|
|
|
166
168
|
| [Lifecycle](docs/lifecycle.md) | Cleanup, consolidation, pruning, LLM maintenance |
|
|
167
169
|
| [Backends](docs/backends.md) | SQLite vs pgvector |
|
|
168
170
|
| [Hermes integration](docs/hermes-integration.md) | Provider, config, installer |
|
|
169
|
-
| [Roadmap](ROADMAP.md) | v0.2.
|
|
171
|
+
| [Roadmap](ROADMAP.md) | v0.2.6 → v1.0.0 |
|
|
170
172
|
| [Benchmarks](benchmarks/RESULTS.md) | 230 ms recall @ 5k, 0 LLM tokens |
|
|
171
173
|
|
|
172
174
|
---
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# PLAN v0.2.5 — Semantic Consolidation + Auto Importance
|
|
2
|
+
|
|
3
|
+
**Version:** 0.2.5 · **Milestone:** Smarter self-maintenance (roadmap v0.3.0 items #4 + #5)
|
|
4
|
+
**Goal:** Replace token-overlap consolidation with semantic (embedding) merging, and auto-estimate memory importance so pruning and health scores reflect real value.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Background (verified from code)
|
|
9
|
+
|
|
10
|
+
- `lifecycle/consolidate.py` uses **Jaccard token overlap only** (`recall/dedup.py:jaccard_similarity`). Two memories with the same meaning but different wording ("deploy target is staging" vs "deploy target is production cluster") are **not** merged → semantic duplicates accumulate.
|
|
11
|
+
- `lifecycle/prune.py` uses **static `m.importance`** (default `0.2`), never estimated from behavior → pruning is blunt.
|
|
12
|
+
- `MemoryClient` has `self.engine.embed()` (fastembed) available — can compute embeddings for consolidation without new deps.
|
|
13
|
+
- `types.py:Memory` has `importance`, `access_count`, `created_at`, `last_accessed_at`, `metadata`.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Phase 1 — Semantic Consolidation (T1–T4)
|
|
18
|
+
|
|
19
|
+
### T1 — cosine helper in `recall/dedup.py`
|
|
20
|
+
|
|
21
|
+
Add `cosine_similarity(a: list[float], b: list[float]) -> float` (pure math, no deps). Reuse in consolidation; keep `jaccard_similarity` for dedup.
|
|
22
|
+
|
|
23
|
+
- Test: identical vectors → 1.0; orthogonal → 0.0; empty → 0.0; negative → clamped.
|
|
24
|
+
|
|
25
|
+
### T2 — semantic merge in `lifecycle/consolidate.py`
|
|
26
|
+
|
|
27
|
+
Extend `consolidate(backend, threshold=0.9, semantic=True)`:
|
|
28
|
+
|
|
29
|
+
- `semantic=True` (default): cluster by `cosine_similarity(embedding_a, embedding_b) >= semantic_threshold` (default 0.85), **falling back to Jaccard** when embeddings are missing (legacy rows, pgvector without vectors).
|
|
30
|
+
- `semantic=False`: current Jaccard behavior (back-compat).
|
|
31
|
+
- Keep existing merge logic (longest content as master, sum access, union tags).
|
|
32
|
+
|
|
33
|
+
- Tests: two memories same meaning different words → merged (embedding similarity high); two unrelated → kept; missing embeddings → falls back to Jaccard; `semantic=False` → Jaccard only.
|
|
34
|
+
|
|
35
|
+
### T3 — CLI flag
|
|
36
|
+
|
|
37
|
+
`luminary-memory lifecycle --semantic/--no-semantic` (default semantic on). Wire through `run_lifecycle()` → `consolidate(semantic=...)`.
|
|
38
|
+
|
|
39
|
+
- Test: CLI flag parse + passes through.
|
|
40
|
+
|
|
41
|
+
### T4 — runner integration
|
|
42
|
+
|
|
43
|
+
`lifecycle/runner.py:run_lifecycle()` passes `semantic` from settings (`LUMINARY_CONSOLIDATE_SEMANTIC`, default `true`). New env var documented.
|
|
44
|
+
|
|
45
|
+
- Test: runner respects env var.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Phase 2 — Auto Importance Estimation (T5–T8)
|
|
50
|
+
|
|
51
|
+
### T5 — estimator module `lifecycle/importance.py`
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
def estimate_importance(
|
|
55
|
+
memory, now=None,
|
|
56
|
+
half_life_hours: float = 24.0, # recency decay
|
|
57
|
+
access_weight: float = 0.4,
|
|
58
|
+
recency_weight: float = 0.3,
|
|
59
|
+
centrality_weight: float = 0.3,
|
|
60
|
+
) -> float:
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`importance = access_norm * access_weight + recency_norm * recency_weight + centrality_norm * centrality_weight`
|
|
64
|
+
|
|
65
|
+
- `access_norm` — `log1p(access_count) / log1p(max_access_in_store)`.
|
|
66
|
+
- `recency_norm` — `exp(-age_hours / half_life_hours)` (same decay shape as temporal recall).
|
|
67
|
+
- `centrality_norm` — graph degree / max degree (relations count; 0 when no graph table).
|
|
68
|
+
- Clamp 0.0–1.0.
|
|
69
|
+
|
|
70
|
+
- Tests: new memory → low importance; accessed many times → high; recent → high; old+never-accessed → low; clamp bounds.
|
|
71
|
+
|
|
72
|
+
### T6 — call estimator on ingest + lifecycle
|
|
73
|
+
|
|
74
|
+
- `api.py:ingest()` — after insert, if importance not explicitly set, `estimate_importance(memory)` and persist (needs backend update — cheap, one row).
|
|
75
|
+
- `run_lifecycle()` — re-estimate all importances before prune so pruning reflects current value.
|
|
76
|
+
- Guard: only when `LUMINARY_IMPORTANCE_AUTO` (default `true`).
|
|
77
|
+
|
|
78
|
+
- Tests: ingest sets importance > 0; lifecycle re-estimates (old memory importance drops); disabled flag keeps static.
|
|
79
|
+
|
|
80
|
+
### T7 — prune uses estimated importance (already does, but now fed live values)
|
|
81
|
+
|
|
82
|
+
No structural change — prune already reads `m.importance`. Add test proving a low-importance memory gets pruned after re-estimation.
|
|
83
|
+
|
|
84
|
+
### T8 — health score uses importance dimension (already reads it)
|
|
85
|
+
|
|
86
|
+
`health_score()` `importance` dimension already uses `m.importance >= prune_min`. With auto-estimation the dimension becomes meaningful. Add test: fresh store → importance high → score contribution healthy.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Phase 3 — Docs + Release (T9–T10)
|
|
91
|
+
|
|
92
|
+
### T9 — Documentation sweep (REQUIRED before release — user rule)
|
|
93
|
+
|
|
94
|
+
- `docs/lifecycle.md` — semantic consolidation section + importance auto-estimation + new env vars.
|
|
95
|
+
- `docs/recall.md` — note cosine in consolidation (dedup still Jaccard).
|
|
96
|
+
- `docs/architecture.md` — update lifecycle pipeline (importance estimation step).
|
|
97
|
+
- `docs/cli.md` — `lifecycle --semantic/--no-semantic`.
|
|
98
|
+
- `README.md` — badges if tests/coverage change; roadmap line v0.2.5.
|
|
99
|
+
- `CHANGELOG.md` — 0.2.5 entry.
|
|
100
|
+
- `ROADMAP.md` — header v0.2.5, tick items #4 (importance) + #5 (consolidation v2).
|
|
101
|
+
- Website badge → v0.2.5.
|
|
102
|
+
- Grep for stale versions across all docs before release.
|
|
103
|
+
|
|
104
|
+
### T10 — Release v0.2.5
|
|
105
|
+
|
|
106
|
+
- Bump `pyproject.toml` + `__init__.py` → 0.2.5.
|
|
107
|
+
- `pytest -q` green, `ruff check` clean, coverage ≥ 90% (DoD).
|
|
108
|
+
- Tag `v0.2.5` → push → GitHub release (Trusted Publisher auto-publishes PyPI).
|
|
109
|
+
- Verify PyPI version + website redeploy.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Definition of Done
|
|
114
|
+
|
|
115
|
+
- [ ] Consolidation merges semantic near-duplicates (verified with same-meaning-different-words test)
|
|
116
|
+
- [ ] Importance auto-estimated on ingest + lifecycle (verified with behavior tests)
|
|
117
|
+
- [ ] Coverage ≥ 90%, ruff clean, all tests green
|
|
118
|
+
- [ ] CLI `--semantic/--no-semantic` works
|
|
119
|
+
- [ ] Docs sweep complete & versions consistent (user rule)
|
|
120
|
+
- [ ] v0.2.5 released via Trusted Publisher (no twine)
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
[](https://pypi.org/project/luminary-memory)
|
|
7
7
|
[](LICENSE)
|
|
8
8
|
[](https://github.com/alertxsto/luminary-memory/actions)
|
|
9
|
-
[](https://github.com/alertxsto/luminary-memory/actions)
|
|
10
|
+
[](https://github.com/alertxsto/luminary-memory)
|
|
11
11
|
[](https://github.com/alertxsto/luminary-memory)
|
|
12
12
|
|
|
13
13
|
**Self-hosted · Private · Budget-aware · Self-maintaining**
|
|
@@ -98,6 +98,8 @@ Every setting has a `LUMINARY_*` env var or a `Settings` object.
|
|
|
98
98
|
| `ttl_default_seconds` | `LUMINARY_TTL_DEFAULT_SECONDS` | `null` |
|
|
99
99
|
| `prune_min_importance` | `LUMINARY_PRUNE_MIN_IMPORTANCE` | `0.2` |
|
|
100
100
|
| `consolidate_jaccard_threshold` | `LUMINARY_CONSOLIDATE_JACCARD_THRESHOLD` | `0.9` |
|
|
101
|
+
| `consolidate_semantic` | `LUMINARY_CONSOLIDATE_SEMANTIC` | `true` |
|
|
102
|
+
| `importance_auto` | `LUMINARY_IMPORTANCE_AUTO` | `true` |
|
|
101
103
|
|
|
102
104
|
See [hermes/SKILL.md](hermes/SKILL.md) for the full provider config table (18 settings).
|
|
103
105
|
|
|
@@ -132,7 +134,7 @@ Built-in lifecycle keeps the store lean. LLM maintenance (optional) keeps it acc
|
|
|
132
134
|
| [Lifecycle](docs/lifecycle.md) | Cleanup, consolidation, pruning, LLM maintenance |
|
|
133
135
|
| [Backends](docs/backends.md) | SQLite vs pgvector |
|
|
134
136
|
| [Hermes integration](docs/hermes-integration.md) | Provider, config, installer |
|
|
135
|
-
| [Roadmap](ROADMAP.md) | v0.2.
|
|
137
|
+
| [Roadmap](ROADMAP.md) | v0.2.6 → v1.0.0 |
|
|
136
138
|
| [Benchmarks](benchmarks/RESULTS.md) | 230 ms recall @ 5k, 0 LLM tokens |
|
|
137
139
|
|
|
138
140
|
---
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **Tagline:** A lightweight, self-hosted memory layer for AI agents.
|
|
4
4
|
|
|
5
|
-
**Current release:** v0.
|
|
5
|
+
**Current release:** v0.2.6 (polish & hardening) · **Tests:** 200+ passing · **Coverage:** 90% · **License:** Apache-2.0
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -33,7 +33,7 @@ Every AI agent deserves durable memory that lives on its own infrastructure. Lum
|
|
|
33
33
|
| Packaging (PyPI, GitHub release, tags) | ✅ v0.1.1 |
|
|
34
34
|
| Website (landing page) | ✅ v0.1.1 |
|
|
35
35
|
| Hermes integration skill | ✅ v0.1.1 |
|
|
36
|
-
| Test coverage | ✅
|
|
36
|
+
| Test coverage | ✅ 90% total |
|
|
37
37
|
|
|
38
38
|
---
|
|
39
39
|
|
|
@@ -102,10 +102,10 @@ Post-MVP audit hardening.
|
|
|
102
102
|
**Goal:** Smarter memory — structure, insight, and visualization.
|
|
103
103
|
|
|
104
104
|
- [ ] Knowledge graph visualization (entity relations browser)
|
|
105
|
-
- [
|
|
105
|
+
- [x] Memory health score (duplicate rate, staleness, importance distribution) — v0.2.4
|
|
106
106
|
- [ ] Multi-user scoping (`user_id` per memory, per-user recall)
|
|
107
|
-
- [
|
|
108
|
-
- [
|
|
107
|
+
- [x] Memory importance auto-estimation (recency + access + semantic centrality) — v0.2.5
|
|
108
|
+
- [x] Consolidation v2: semantic-aware merging (not just Jaccard token overlap) — v0.2.5
|
|
109
109
|
|
|
110
110
|
---
|
|
111
111
|
|
|
@@ -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`, `run_maintenance` (LLM curation), `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), `health_score` (store health report), `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`.
|
|
@@ -42,5 +42,9 @@ See [backends.md](backends.md).
|
|
|
42
42
|
Three maintenance passes, orchestrated by `run_lifecycle()`:
|
|
43
43
|
|
|
44
44
|
- **cleanup** — remove TTL-expired memories.
|
|
45
|
-
- **consolidate** — merge near-duplicates (Jaccard
|
|
46
|
-
- **prune** — drop low-importance or least-recently-used memories.
|
|
45
|
+
- **consolidate** — merge near-duplicates (Jaccard or embedding-cosine, semantic by default).
|
|
46
|
+
- **prune** — drop low-importance or least-recently-used memories (importance auto-estimated from access, recency, centrality).
|
|
47
|
+
|
|
48
|
+
Optional **LLM maintenance** (`run_maintenance()`, or provider `auto_maintain`)
|
|
49
|
+
reviews the whole store and keeps/updates/deletes facts semantically.
|
|
50
|
+
`health_score()` reports store quality (0-100) across five dimensions.
|
|
@@ -46,9 +46,30 @@ Most recent first.
|
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
48
|
luminary-memory lifecycle
|
|
49
|
+
luminary-memory lifecycle --no-semantic # Jaccard-only consolidation
|
|
49
50
|
```
|
|
50
51
|
|
|
51
|
-
Runs cleanup + consolidate + prune, prints
|
|
52
|
+
Runs cleanup + consolidate + prune (plus importance re-estimation), prints
|
|
53
|
+
counts. `--semantic/--no-semantic` toggles embedding-cosine consolidation
|
|
54
|
+
(default: semantic on).
|
|
55
|
+
|
|
56
|
+
### export
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
luminary-memory export --path backup.json
|
|
60
|
+
luminary-memory export --path backup.json --no-embeddings
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Exports all memories to a JSON file (backup/migration). `--no-embeddings`
|
|
64
|
+
skips embedding vectors to keep the file small.
|
|
65
|
+
|
|
66
|
+
### import
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
luminary-memory import --path backup.json
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Imports memories from a JSON export (recomputes embeddings when absent).
|
|
52
73
|
|
|
53
74
|
### stats
|
|
54
75
|
|
|
@@ -58,6 +79,17 @@ luminary-memory stats
|
|
|
58
79
|
|
|
59
80
|
Prints store statistics as JSON.
|
|
60
81
|
|
|
82
|
+
### health
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
luminary-memory health
|
|
86
|
+
luminary-memory health --json
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Prints a 0-100 store health score with a per-dimension breakdown (duplicate
|
|
90
|
+
rate, staleness, importance, graph density, size) and actionable
|
|
91
|
+
recommendations. `--json` emits the raw report.
|
|
92
|
+
|
|
61
93
|
## Exit codes
|
|
62
94
|
|
|
63
95
|
- `0` — success.
|
|
@@ -96,6 +96,8 @@ cp hermes/SKILL.md ~/.hermes/skills/luminary-memory/SKILL.md
|
|
|
96
96
|
1. **Ingest on tool call** — after learning a durable fact (preference, environment detail), call `client.ingest(...)`.
|
|
97
97
|
2. **Recall into the system prompt** — before answering, call `client.recall(query)` and inject the top memories as context.
|
|
98
98
|
3. **Lifecycle via cron** — schedule `luminary-memory lifecycle` to keep the store clean.
|
|
99
|
+
4. **Monitor health** — `luminary-memory health` (or `client.health_score()`) reports store quality; run it in a cron to catch drift.
|
|
100
|
+
5. **LLM curation (optional)** — with `ingest_llm` + `auto_maintain`, turns are curated and stale facts pruned automatically.
|
|
99
101
|
|
|
100
102
|
## Example
|
|
101
103
|
|
|
@@ -12,6 +12,8 @@ A lightweight, self-hosted memory layer for AI agents.
|
|
|
12
12
|
|
|
13
13
|
- Four retrieval strategies fused into one ranked recall (semantic, keyword, temporal, graph).
|
|
14
14
|
- **First-class Hermes Agent memory provider** — auto-recall every turn, auto-save every session, zero LLM tokens per turn (`memory.provider: luminary`).
|
|
15
|
+
- **LLM memory curation** — optional `ingest_llm` drops chit-chat and stores factual summaries; `auto_maintain` prunes stale/duplicate facts at session end.
|
|
16
|
+
- **Health score** — `health_score()` / `luminary-memory health` reports store quality (0-100) with recommendations.
|
|
15
17
|
- SQLite out of the box, optional pgvector for scale.
|
|
16
18
|
- Local CPU embeddings via ONNX — no GPU, no cloud.
|
|
17
19
|
- Configurable token budget so memory never overflows the agent's context.
|
|
@@ -11,7 +11,22 @@ Removes memories whose `ttl_seconds` has elapsed.
|
|
|
11
11
|
|
|
12
12
|
### consolidate — merge near-duplicates
|
|
13
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.
|
|
14
|
+
Clusters memories by Jaccard similarity (`LUMINARY_CONSOLIDATE_JACCARD_THRESHOLD`, default 0.9) **or embedding-cosine similarity** (`LUMINARY_CONSOLIDATE_SEMANTIC`, default `true`) and merges each cluster into a single memory — keeping the longest content, summing access counts, and unioning tags. Semantic mode (default) merges paraphrases that token overlap would miss; it falls back to Jaccard when embeddings are missing. Disable with `LUMINARY_CONSOLIDATE_SEMANTIC=false` or `luminary-memory lifecycle --no-semantic`.
|
|
15
|
+
|
|
16
|
+
### importance — auto-estimation
|
|
17
|
+
|
|
18
|
+
On ingest and before each prune, each memory's importance is estimated from behavior:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
importance = access_norm × 0.4 + recency_norm × 0.3 + centrality_norm × 0.3
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- `access_norm` — `log1p(access_count)` normalized to the store's max.
|
|
25
|
+
- `recency_norm` — `exp(-age_hours / 24h)` (fresh memories score higher).
|
|
26
|
+
- `centrality_norm` — graph relation degree normalized (0 when no graph).
|
|
27
|
+
|
|
28
|
+
Disable with `LUMINARY_IMPORTANCE_AUTO=false`. Pruning and the health score's
|
|
29
|
+
importance dimension both use these live values.
|
|
15
30
|
|
|
16
31
|
### prune — drop low-value memories
|
|
17
32
|
|
|
@@ -31,6 +31,9 @@ for memory, score in zip(result.memories, result.scores):
|
|
|
31
31
|
# 3. maintain the store
|
|
32
32
|
client.run_lifecycle()
|
|
33
33
|
|
|
34
|
+
# 4. check store health (0-100 score + recommendations)
|
|
35
|
+
print(client.health_score())
|
|
36
|
+
|
|
34
37
|
client.close()
|
|
35
38
|
```
|
|
36
39
|
|
|
@@ -40,8 +43,44 @@ client.close()
|
|
|
40
43
|
luminary-memory add "The deploy target is the staging cluster" --tags deploy
|
|
41
44
|
luminary-memory recall "where do we deploy?" --json
|
|
42
45
|
luminary-memory stats
|
|
46
|
+
luminary-memory health
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Backup & restore
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
luminary-memory export --path backup.json
|
|
53
|
+
luminary-memory import --path backup.json
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`export` writes all memories to a JSON file (migration/backup); `import`
|
|
57
|
+
restores them (recomputing embeddings when absent).
|
|
58
|
+
|
|
59
|
+
## Optional: LLM memory curation
|
|
60
|
+
|
|
61
|
+
By default every ingest is stored verbatim (zero LLM cost). To have an LLM
|
|
62
|
+
evaluate each turn — dropping chit-chat and storing concise factual summaries
|
|
63
|
+
instead of raw transcripts — enable `ingest_llm` and `auto_maintain`:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install "luminary-memory[hermes]"
|
|
43
67
|
```
|
|
44
68
|
|
|
69
|
+
```json
|
|
70
|
+
// ~/.hermes/luminary/config.json (Hermes provider)
|
|
71
|
+
{
|
|
72
|
+
"ingest_llm": true,
|
|
73
|
+
"auto_maintain": true,
|
|
74
|
+
"llm_base_url": "https://api.commandcode.ai/provider/v1",
|
|
75
|
+
"llm_model": "deepseek/deepseek-v4-flash",
|
|
76
|
+
"llm_api_key": "your-key"
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Any OpenAI-compatible endpoint works. `auto_maintain` reviews the store at
|
|
81
|
+
session end — keeping current facts, updating changed ones, deleting stale or
|
|
82
|
+
duplicate memories.
|
|
83
|
+
|
|
45
84
|
## Configuration
|
|
46
85
|
|
|
47
86
|
All settings accept a `LUMINARY_*` env var:
|
|
@@ -86,11 +86,15 @@ client.run_lifecycle()
|
|
|
86
86
|
|
|
87
87
|
# LLM review: keep current facts, update changed ones, delete stale/duplicates
|
|
88
88
|
client.run_maintenance() # requires an LLM enricher (ingest_llm)
|
|
89
|
+
|
|
90
|
+
# health check: 0-100 score with per-dimension breakdown + recommendations
|
|
91
|
+
report = client.health_score()
|
|
89
92
|
```
|
|
90
93
|
|
|
91
94
|
In the Hermes provider, `auto_maintain: true` (plus `ingest_llm: true`) runs
|
|
92
95
|
`run_maintenance()` automatically at every session end — no manual trigger
|
|
93
|
-
needed. Results are logged to `~/.hermes/luminary/luminary.log`.
|
|
96
|
+
needed. Results are logged to `~/.hermes/luminary/luminary.log`. CLI:
|
|
97
|
+
`luminary-memory health` (human bar) or `--json`.
|
|
94
98
|
|
|
95
99
|
## Configuration — Tweaks
|
|
96
100
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import logging
|
|
3
4
|
from datetime import UTC
|
|
4
5
|
from typing import TYPE_CHECKING
|
|
5
6
|
|
|
@@ -11,18 +12,16 @@ from luminary_memory.ingest.llm import LLMEnricher, NoopEnricher
|
|
|
11
12
|
from luminary_memory.ingest.whitelist import WhitelistFilter
|
|
12
13
|
from luminary_memory.types import Memory, RecallResult
|
|
13
14
|
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
14
16
|
|
|
15
|
-
def _try_index_graph(backend, memory: Memory) -> None:
|
|
16
|
-
import logging
|
|
17
17
|
|
|
18
|
+
def _try_index_graph(backend, memory: Memory) -> None:
|
|
18
19
|
from luminary_memory.recall.graph import index_memory_entities
|
|
19
20
|
|
|
20
21
|
try:
|
|
21
22
|
index_memory_entities(backend, memory)
|
|
22
23
|
except Exception: # noqa: BLE001 -- graph indexing is best-effort; never abort ingest
|
|
23
|
-
|
|
24
|
-
"graph indexing failed for memory %s (non-fatal)", memory.id
|
|
25
|
-
)
|
|
24
|
+
logger.warning("graph indexing failed for memory %s (non-fatal)", memory.id)
|
|
26
25
|
|
|
27
26
|
if TYPE_CHECKING:
|
|
28
27
|
from luminary_memory.backends.base import MemoryBackend
|
|
@@ -77,6 +76,10 @@ class MemoryClient:
|
|
|
77
76
|
ttl_seconds=self.settings.ttl_default_seconds,
|
|
78
77
|
embedding=self.engine.embed(content),
|
|
79
78
|
)
|
|
79
|
+
if self.settings.importance_auto:
|
|
80
|
+
from luminary_memory.lifecycle.importance import estimate_importance
|
|
81
|
+
|
|
82
|
+
m.importance = estimate_importance(m)
|
|
80
83
|
mid = self.backend.add(m)
|
|
81
84
|
m.id = mid
|
|
82
85
|
_try_index_graph(self.backend, m)
|
|
@@ -250,10 +253,10 @@ class MemoryClient:
|
|
|
250
253
|
def count(self) -> int:
|
|
251
254
|
return self.backend.count()
|
|
252
255
|
|
|
253
|
-
def run_lifecycle(self) -> dict[str, int]:
|
|
256
|
+
def run_lifecycle(self, semantic: bool | None = None) -> dict[str, int]:
|
|
254
257
|
from luminary_memory.lifecycle.runner import run_lifecycle
|
|
255
258
|
|
|
256
|
-
return run_lifecycle(self.backend, self.settings)
|
|
259
|
+
return run_lifecycle(self.backend, self.settings, semantic=semantic)
|
|
257
260
|
|
|
258
261
|
def health_score(self) -> dict:
|
|
259
262
|
"""Store health report: overall 0-100 plus per-dimension breakdown.
|
|
@@ -419,7 +422,11 @@ class MemoryClient:
|
|
|
419
422
|
"""Import memories from *path* (recomputes embeddings when absent)."""
|
|
420
423
|
from luminary_memory.export import import_memories
|
|
421
424
|
|
|
422
|
-
|
|
425
|
+
try:
|
|
426
|
+
return import_memories(self.backend, path, engine=self.engine)
|
|
427
|
+
except Exception:
|
|
428
|
+
logger.exception("import_memories failed for %s", path)
|
|
429
|
+
raise
|
|
423
430
|
|
|
424
431
|
def close(self) -> None:
|
|
425
432
|
self.backend.close()
|
|
@@ -435,7 +442,7 @@ class MemoryClient:
|
|
|
435
442
|
if n_limit < 0:
|
|
436
443
|
raise ValueError("limit must be >= 0 (0 means unlimited)")
|
|
437
444
|
if n_limit == 0:
|
|
438
|
-
n_limit =
|
|
445
|
+
n_limit = None # unlimited — backends treat None as "no limit"
|
|
439
446
|
from luminary_memory.recall.dedup import dedup_jaccard
|
|
440
447
|
from luminary_memory.recall.fusion import reciprocal_rank_fusion
|
|
441
448
|
from luminary_memory.recall.graph import graph_recall
|
|
@@ -450,6 +457,7 @@ class MemoryClient:
|
|
|
450
457
|
planner_threshold = float(getattr(self.settings, "query_planner_keyword_threshold", 0.9))
|
|
451
458
|
|
|
452
459
|
eff = n_limit
|
|
460
|
+
temporal_limit = (eff * 2) if eff is not None else None
|
|
453
461
|
enabled = None
|
|
454
462
|
|
|
455
463
|
strategies: list[list[tuple]] = []
|
|
@@ -457,7 +465,7 @@ class MemoryClient:
|
|
|
457
465
|
strat_fns = [
|
|
458
466
|
("semantic", lambda: semantic_recall(self.backend, self.engine, query, limit=eff)),
|
|
459
467
|
("keyword", lambda: keyword_recall(self.backend, query, limit=eff)),
|
|
460
|
-
("temporal", lambda: temporal_recall(self.backend, limit=
|
|
468
|
+
("temporal", lambda: temporal_recall(self.backend, limit=temporal_limit)),
|
|
461
469
|
("graph", lambda: graph_recall(self.backend, query, limit=eff)),
|
|
462
470
|
]
|
|
463
471
|
|