loop-memory 0.4.18__tar.gz → 0.4.20__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.
- {loop_memory-0.4.18 → loop_memory-0.4.20}/PKG-INFO +6 -1
- {loop_memory-0.4.18 → loop_memory-0.4.20}/README.md +4 -0
- loop_memory-0.4.20/loop_memory/bench/fixtures/default.json +363 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/bench/runner.py +25 -3
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/cli/commands/bench.py +5 -1
- loop_memory-0.4.20/loop_memory/serve/static/css/app.css +4322 -0
- loop_memory-0.4.20/loop_memory/serve/static/css/layout.css +1876 -0
- loop_memory-0.4.20/loop_memory/serve/static/i18n/en.json +741 -0
- loop_memory-0.4.20/loop_memory/serve/static/i18n/zh.json +741 -0
- loop_memory-0.4.20/loop_memory/serve/static/img/logo-light.svg +39 -0
- loop_memory-0.4.20/loop_memory/serve/static/img/logo-mark.svg +57 -0
- loop_memory-0.4.20/loop_memory/serve/static/img/logo.svg +50 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/App.js +268 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/api.js +191 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/components/Dashboard.js +1361 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/components/Diagnostic.js +175 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/components/IngestPopover.js +208 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/components/KnowledgeGraph.js +885 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/components/RingMeter.js +51 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/components/RunStrip.js +31 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/components/SectionTitle.js +26 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/components/Settings.js +1062 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/components/Sidebar.js +221 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/components/Tabs.js +49 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/components/Timeline.js +236 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/components/Toast.js +16 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/components/TopBar.js +235 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/components/Wiki.js +815 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/components/WikiEditor.js +189 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/composables/useI18n.js +15 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/lib/sanitize.js +118 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/lib/vue.esm-browser.prod.js +9 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/main.js +20 -0
- loop_memory-0.4.20/loop_memory/serve/static/js/store.js +267 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory.egg-info/PKG-INFO +6 -1
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory.egg-info/SOURCES.txt +31 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory.egg-info/requires.txt +1 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/pyproject.toml +19 -3
- loop_memory-0.4.20/tests/test_packaging_2026_11_01.py +359 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/LICENSE +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/__init__.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/backends/__init__.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/backends/embedding.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/backends/sentence_embedder.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/backends/vector_store.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/bench/__init__.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/cli/__init__.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/cli/_common.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/cli/commands/__init__.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/cli/commands/cognitive.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/cli/commands/diag.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/cli/commands/doctor.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/cli/commands/graph.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/cli/commands/hooks.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/cli/commands/read.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/cli/commands/rules.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/cli/commands/serve.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/cli/commands/write.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/cli/main.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/decorators.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/doctor.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/engine/__init__.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/engine/loop.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/engine/reflect.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/examples/__init__.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/examples/demo.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/export/__init__.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/export/memory_md.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/graph/__init__.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/graph/build.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/graph/extract.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/ingest/__init__.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/ingest/loader.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/ingest/pipeline.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/jobs/__init__.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/jobs/cognitive.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/jobs/compact.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/jobs/consolidate.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/jobs/contradiction.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/jobs/dream.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/jobs/evolution.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/jobs/graph.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/jobs/llm_compact_pass.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/jobs/llm_consolidate.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/jobs/scheduler.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/llm/__init__.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/llm/base.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/llm/openai_adapter.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/llm/providers.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/mcp/__init__.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/memory/__init__.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/memory/types.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/privacy/__init__.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/privacy/private.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/privacy/redact.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/py.typed +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/sdk.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/sdk_extensions.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/security/__init__.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/security/secrets.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/serve/__init__.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/serve/app.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/serve/handlers.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/serve/routes/_shared.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/serve/routes/admin.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/serve/routes/cognitive.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/serve/routes/export.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/serve/routes/graph.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/serve/routes/insights.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/serve/routes/memories.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/serve/routes/sessions.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/serve/routes/system.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/serve/routes/wiki.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/serve/static/__init__.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/serve/static/index.html +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/serve/watcher.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/storage/__init__.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/storage/lexical.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/storage/retrieval.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/storage/snapshot.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/storage/sqlite_store.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/wiki/__init__.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/wiki/backfill.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/wiki/classifier.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/wiki/prompts.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory/wiki/scope.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory.egg-info/dependency_links.txt +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory.egg-info/entry_points.txt +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/loop_memory.egg-info/top_level.txt +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/setup.cfg +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_admin_ingest_route.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_agent_memory_api.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_agent_memory_sdk.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_agents_init.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_audit_triggers_2026_09_27.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_auth_token_rotate.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_bench_2026_11_01.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_busy_timeout_2026_09_24.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_cache_cap_2026_09_23.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_cli_audit_supersede.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_cli_export_okf.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_cli_init.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_cli_memory_stats.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_cli_recall_as_of.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_cli_recall_paths.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_cli_recall_verbose.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_cli_rules.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_cli_snapshot.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_cli_v7.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_cli_version.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_content_hash_dedup_2026_10_18.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_contradictions.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_decorators_2026_10_18.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_doctor_2026_10_12.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_dream_2026_10_25.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_dream_cli_http_2026_10_25.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_evolution.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_evolution_quality.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_expanduser.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_export_ask.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_export_okf.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_graph.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_healthz_readyz_2026_09_24.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_import_okf_2026_09_27.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_ingest.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_latest_only_2026_10_25.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_lexical_ranking_2026_11_01.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_llm_consolidator.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_llm_fingerprint_not_persisted.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_llm_providers.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_llm_test_endpoint.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_loop.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_mcp.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_memories_pagination.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_memory_stats.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_openclaw_loader.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_package_version.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_path_safety_2026_09_23.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_recall_as_of.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_recall_budget_2026_10_05.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_recall_paths.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_recall_provenance.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_recall_validity_http_2026_10_18.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_reflection.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_repo_framing.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_retrieval_channels_2026_10_18.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_routes_audit_2026_09_13.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_routes_audit_2026_09_20.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_score_api.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_scoring_v2.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_secrets.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_serve_app.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_serve_handlers.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_session_order.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_short_query.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_snapshot.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_store.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_subgraph_hops_2026_09_23.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_subject_key_2026_10_25.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_summarization.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_supersession_chain.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_universal_memory.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_vector_store.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_watcher.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_wiki_classifier.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_wiki_export_escape.py +0 -0
- {loop_memory-0.4.18 → loop_memory-0.4.20}/tests/test_wiki_prompts.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: loop-memory
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.20
|
|
4
4
|
Summary: A general-purpose, local memory system for every AI agent you run. Loop Memory auto-captures conversations from Codex / Claude / Hermes / OpenClaw, scores them by importance × recency × usage × feedback, distils them into a curated wiki, and serves everything from a single web UI.
|
|
5
5
|
Author: Loop Memory contributors <loop-memory@users.noreply.github.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -34,6 +34,7 @@ Provides-Extra: dev
|
|
|
34
34
|
Requires-Dist: pytest~=8.0; extra == "dev"
|
|
35
35
|
Requires-Dist: coverage~=7.0; extra == "dev"
|
|
36
36
|
Requires-Dist: ruff~=0.5; extra == "dev"
|
|
37
|
+
Requires-Dist: build~=1.2; extra == "dev"
|
|
37
38
|
Provides-Extra: all
|
|
38
39
|
Requires-Dist: openai~=1.30; extra == "all"
|
|
39
40
|
Requires-Dist: chromadb~=0.4; extra == "all"
|
|
@@ -66,6 +67,10 @@ Dynamic: license-file
|
|
|
66
67
|
|
|
67
68
|
---
|
|
68
69
|
|
|
70
|
+
> **What's new in 0.4.20** — **the packaging test works on the oldest supported Python** (audit 2026-11-01, patch follow-up to 0.4.19). The 0.4.19 packaging test parsed `pyproject.toml` with `tomllib`, which is stdlib only from Python **3.11** onwards, while the project declares `requires-python = ">=3.10"` — so the 3.10 CI job failed with `ModuleNotFoundError: No module named 'tomllib'` while 3.11 and 3.12 passed, the classic shape of a bug that only bites the oldest supported interpreter. The check now reads the `[tool.setuptools.package-data]` table directly (it is a flat mapping of package name to a list of glob strings), and a new case runs it in a subprocess with `tomllib` blocked at the import system level, so the 3.10 path is exercised on every version rather than only on the 3.10 runner. No behaviour change — the 0.4.19 distribution is unchanged and correct. 1 new regression case; suite at **1004 passed / 4 skipped**. [Full CHANGELOG →](./CHANGELOG.md#0420---2026-11-01)
|
|
71
|
+
|
|
72
|
+
> **What's new in 0.4.19** — **the PyPI wheel now ships the whole dashboard and the benchmark fixture** (audit 2026-11-01, packaging follow-up to 0.4.18). The 0.4.18 wheel contained exactly two data files — `loop_memory/py.typed` and `loop_memory/serve/static/index.html` — because the package-data declaration was `loop_memory = ["py.typed", "serve/static/*.html"]`, and that glob is **not recursive**. Every CSS, JavaScript, i18n and image asset and the new benchmark fixture were **missing from the distribution**. Reproduced against the published wheel: `pip install loop-memory` started a server whose `GET /` returned `200` but whose `GET /static/js/main.js` and `GET /static/css/app.css` both returned **404** — a blank dashboard — and `loop-memory bench` crashed with `FileNotFoundError` on the packaged fixture. The declaration is now per package with recursive globs (`"loop_memory.serve.static" = ["**/*.html", "**/*.css", "**/*.js", "**/*.json", "**/*.svg"]`, `"loop_memory.bench" = ["fixtures/*.json"]`), so a new asset is picked up without anyone remembering to extend it, and the wheel contains all 32 non-Python files in the package tree. A new packaging test builds the real wheel and sdist **from a pristine source copy** — setuptools caches its file list in `egg-info/SOURCES.txt` and packages a stale `build/lib`, so building in the working tree would make the test pass for the wrong reason — and asserts every non-Python file actually reaches the distribution, then unpacks the wheel and runs the benchmark out of it. 8 new regression cases; suite at **1003 passed / 4 skipped**. [Full CHANGELOG →](./CHANGELOG.md#0419---2026-11-01)
|
|
73
|
+
|
|
69
74
|
> **What's new in 0.4.18** — **the BM25 channel no longer publishes a score that collapses to zero, and there is finally a benchmark that measures it** (audit 2026-11-01, weekly ecosystem sync). SQLite's FTS5 `bm25()` computes the classic idf `log((N - n + 0.5) / (n + 0.5))` and **clamps every non-positive idf to `1e-6`** — so a query term present in 49 % of the corpus keeps a real idf while one present in 51 % has its weight annihilated, a factor of ~40 000 between two almost equally discriminative terms. With the `trigram` tokenizer that is the *normal* case for CJK (`知识图` alone occurs in more than half of any knowledge-graph-heavy corpus), and the visible symptom was the 0.4.15 evidence chain reporting **`bm25: 0.0`** — "this hit because bm25 0.0". The published score is now a textbook BM25 (`k1 = 1.2`, `b = 0.75`) with the Lucene / BM25+ idf `log(1 + (N - n + 0.5) / (n + 0.5))`, which is strictly positive for every term, so the scale cannot collapse. **A document that contains the query verbatim now outranks one that merely contains the same scattered trigrams** — the transferable half of `rohitg00/agentmemory` v0.9.31's prefix-score fix (2026-10-26: strict recall-all@5 **65.74 % → 84.68 %**, +18.94 pp), whose release note is careful to call its numbers "component retrieval results on inspected development data, not held-out answer accuracy". The `LIKE` fallback and the FTS5 hits are finally on the **same scale** (a `LIKE`-only hit used to publish the memory's `importance` under the name `bm25`). The candidate set is unchanged — the fix re-ranks, it does not add or drop documents. New **`loop-memory bench`**: a deterministic retrieval-quality benchmark over a fixed, versioned fixture (30 memories, 12 wiki pages, 11 judged queries) reporting **recall@1/3/5, MRR@10, nDCG@10** with the fixture's revision and the SHA-256 of its bytes; the run is fully offline (**no provider API is called, $0, no network**) and builds its store in a throwaway directory. On the shipped fixture the channel scored **MRR@10 = 0.6061, recall@1 = 0.2273, nDCG@10 = 0.7078** before and **1.0000 / 0.9545 / 1.0000** after — and the report keeps the raw FTS5 score next to the repaired one, so the defect stays visible instead of being quietly fixed and forgotten. Source: *agentmemory* v0.9.31 evaluation methodology + the FTS5 idf clamp in SQLite's `fts5_aux.c` (Apache-2.0 — *pattern only, no code copied*); honcho v3.4.1's "a filter must not silently drop a term" and v3.5.0's "a conclusion must cite its source" were reviewed and need no code (AGPL-3.0 — *pattern only*). 37 new regression cases; suite at **995 passed / 4 skipped**. [Full CHANGELOG →](./CHANGELOG.md#0418---2026-11-01)
|
|
70
75
|
> **What's new in 0.4.17** — **a dry-run dream pass no longer writes to the database** (audit 2026-10-25). `loop-memory dream` and `POST /api/v1/dream` have always documented themselves as *"nothing is written unless `apply=True`"* — but the pass's decay stage called `MemoryStore.rescore_all()` unconditionally, which rewrote the `memories.score` column of **every** row while claiming to only inspect the store. The expiry and merge decisions were correctly left unapplied; the score decay was not. `rescore_all()` now takes a `dry_run` flag: it computes every new score and reports how many rows *would* change but issues no `UPDATE`, and the dream pass runs it with `dry_run=not apply`. A dry run still reports the size of the decay job through `report.rescored` while touching nothing. The contract wording is now exact rather than aspirational — a dry run changes **no memory data** (no expiry, no supersession, no rescore); the single write it still performs is one `cognitive_audit` row per proposed action with `action="suggest"` instead of `"applied"`, because that row *is* the reviewable plan the dashboard shows before you commit to it (`record_audit=False` suppresses even that). Also fixed in this release: `publish.yml` now sends `make_latest` as a JSON **boolean** (`gh api -F`) instead of a JSON **string** — GitHub's release API ignores the string form, so the "mark as latest" step had been returning 200 and reporting success while promoting nothing, leaving `/releases/latest` pointing at v0.4.14 through the 0.4.15 and 0.4.16 ships. The step now also re-reads `/releases/latest` and fails the job when the tag does not match, instead of trusting the exit code. 3 new regression cases (`tests/test_dream_2026_10_25.py`); suite at **958 passed / 4 skipped**. [Full CHANGELOG →](./CHANGELOG.md#0417---2026-10-25)
|
|
71
76
|
> **What's new in 0.4.16** — **subject-key logical fact slots + `latest_only` recall + the "dream" sleep-time consolidation pass** (audit 2026-10-25, weekly ecosystem sync — Mem0 v2.2.1 "Dream", arXiv 2605.27366 "In Defense of Forgotten", Zep/Graphiti temporal edges, Letta memory blocks). New nullable `subject_key` column on `memories` names the *logical slot* a fact occupies (`"editor.preference"`); `upsert_memory(subject_key=...)` makes the new row the live version and soft-supersedes the previous one with a `superseded_by` pointer, so the version history survives and `recall_as_of()` can still answer "what did we believe last week?". The content-hash dedup path is now slot-aware — two different slots holding the same text stay two rows. `SCHEMA_VERSION` bumps 12 → 13 with an idempotent in-place migration. New `latest_only=False` flag on `recall()` / `recall_hybrid()` (Mem0 v2 `SearchMemoryOptions.latest_only` parity) collapses the answer in three layers: rows outside their validity window at *now* are dropped, a subject-key slot resolves to its newest **live** row (looked up in the store, so a stale version never comes back just because it matched the query better), and near-duplicate texts cluster by token Jaccard (default 0.85) with the newest row winning. Wired through `GET /api/recall?latest_only=1`, `GET /api/v1/recall?latest_only=1`, `loop-memory recall --latest-only` and `MemoryClient.recall(latest_only=True)`. New `loop_memory/jobs/dream.py` implements the sleep-time consolidation Mem0 v2 calls the "Dream" — deterministic, zero-dep and **non-destructive**: it decays scores, then judges every row past its deadline on **utility** (`0.45 × importance + 0.30 × usage + 0.25 × recency`) so a frequently-recalled old fact is *protected* while a never-used stale row is **soft-expired** (`invalid_at` stamped, row kept, history kept) instead of deleted, and finally merges near-duplicates into a supersession chain. Run it with `loop-memory dream [--apply] [--json]` or `POST /api/v1/dream` — it is a **dry run by default** and every decision lands in `cognitive_audit` so the dashboard can show and revert it. New store primitives `expire_memory()` / `revive_memory()`, and `memory_stats()` now reports `subject_key`, `ttl`, `valid_at`, `invalid_at` and a derived `ttl_state`. **Also fixed a real defect**: the hybrid recall path (BM25 / LIKE / semantic channels) did not filter `superseded_by`, so `recall_hybrid()` — the production path behind the CLI, MCP server and dashboard — returned both versions of a superseded fact. Source: Mem0 v2.2.1 Dream + ADD-only architecture + arXiv 2605.27366 utility-aware eviction + Zep/Graphiti bi-temporal edges (pattern only, no code copied). 77 new regression cases. [Full CHANGELOG →](./CHANGELOG.md#0416---2026-10-25)
|
|
@@ -22,6 +22,10 @@
|
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
|
+
> **What's new in 0.4.20** — **the packaging test works on the oldest supported Python** (audit 2026-11-01, patch follow-up to 0.4.19). The 0.4.19 packaging test parsed `pyproject.toml` with `tomllib`, which is stdlib only from Python **3.11** onwards, while the project declares `requires-python = ">=3.10"` — so the 3.10 CI job failed with `ModuleNotFoundError: No module named 'tomllib'` while 3.11 and 3.12 passed, the classic shape of a bug that only bites the oldest supported interpreter. The check now reads the `[tool.setuptools.package-data]` table directly (it is a flat mapping of package name to a list of glob strings), and a new case runs it in a subprocess with `tomllib` blocked at the import system level, so the 3.10 path is exercised on every version rather than only on the 3.10 runner. No behaviour change — the 0.4.19 distribution is unchanged and correct. 1 new regression case; suite at **1004 passed / 4 skipped**. [Full CHANGELOG →](./CHANGELOG.md#0420---2026-11-01)
|
|
26
|
+
|
|
27
|
+
> **What's new in 0.4.19** — **the PyPI wheel now ships the whole dashboard and the benchmark fixture** (audit 2026-11-01, packaging follow-up to 0.4.18). The 0.4.18 wheel contained exactly two data files — `loop_memory/py.typed` and `loop_memory/serve/static/index.html` — because the package-data declaration was `loop_memory = ["py.typed", "serve/static/*.html"]`, and that glob is **not recursive**. Every CSS, JavaScript, i18n and image asset and the new benchmark fixture were **missing from the distribution**. Reproduced against the published wheel: `pip install loop-memory` started a server whose `GET /` returned `200` but whose `GET /static/js/main.js` and `GET /static/css/app.css` both returned **404** — a blank dashboard — and `loop-memory bench` crashed with `FileNotFoundError` on the packaged fixture. The declaration is now per package with recursive globs (`"loop_memory.serve.static" = ["**/*.html", "**/*.css", "**/*.js", "**/*.json", "**/*.svg"]`, `"loop_memory.bench" = ["fixtures/*.json"]`), so a new asset is picked up without anyone remembering to extend it, and the wheel contains all 32 non-Python files in the package tree. A new packaging test builds the real wheel and sdist **from a pristine source copy** — setuptools caches its file list in `egg-info/SOURCES.txt` and packages a stale `build/lib`, so building in the working tree would make the test pass for the wrong reason — and asserts every non-Python file actually reaches the distribution, then unpacks the wheel and runs the benchmark out of it. 8 new regression cases; suite at **1003 passed / 4 skipped**. [Full CHANGELOG →](./CHANGELOG.md#0419---2026-11-01)
|
|
28
|
+
|
|
25
29
|
> **What's new in 0.4.18** — **the BM25 channel no longer publishes a score that collapses to zero, and there is finally a benchmark that measures it** (audit 2026-11-01, weekly ecosystem sync). SQLite's FTS5 `bm25()` computes the classic idf `log((N - n + 0.5) / (n + 0.5))` and **clamps every non-positive idf to `1e-6`** — so a query term present in 49 % of the corpus keeps a real idf while one present in 51 % has its weight annihilated, a factor of ~40 000 between two almost equally discriminative terms. With the `trigram` tokenizer that is the *normal* case for CJK (`知识图` alone occurs in more than half of any knowledge-graph-heavy corpus), and the visible symptom was the 0.4.15 evidence chain reporting **`bm25: 0.0`** — "this hit because bm25 0.0". The published score is now a textbook BM25 (`k1 = 1.2`, `b = 0.75`) with the Lucene / BM25+ idf `log(1 + (N - n + 0.5) / (n + 0.5))`, which is strictly positive for every term, so the scale cannot collapse. **A document that contains the query verbatim now outranks one that merely contains the same scattered trigrams** — the transferable half of `rohitg00/agentmemory` v0.9.31's prefix-score fix (2026-10-26: strict recall-all@5 **65.74 % → 84.68 %**, +18.94 pp), whose release note is careful to call its numbers "component retrieval results on inspected development data, not held-out answer accuracy". The `LIKE` fallback and the FTS5 hits are finally on the **same scale** (a `LIKE`-only hit used to publish the memory's `importance` under the name `bm25`). The candidate set is unchanged — the fix re-ranks, it does not add or drop documents. New **`loop-memory bench`**: a deterministic retrieval-quality benchmark over a fixed, versioned fixture (30 memories, 12 wiki pages, 11 judged queries) reporting **recall@1/3/5, MRR@10, nDCG@10** with the fixture's revision and the SHA-256 of its bytes; the run is fully offline (**no provider API is called, $0, no network**) and builds its store in a throwaway directory. On the shipped fixture the channel scored **MRR@10 = 0.6061, recall@1 = 0.2273, nDCG@10 = 0.7078** before and **1.0000 / 0.9545 / 1.0000** after — and the report keeps the raw FTS5 score next to the repaired one, so the defect stays visible instead of being quietly fixed and forgotten. Source: *agentmemory* v0.9.31 evaluation methodology + the FTS5 idf clamp in SQLite's `fts5_aux.c` (Apache-2.0 — *pattern only, no code copied*); honcho v3.4.1's "a filter must not silently drop a term" and v3.5.0's "a conclusion must cite its source" were reviewed and need no code (AGPL-3.0 — *pattern only*). 37 new regression cases; suite at **995 passed / 4 skipped**. [Full CHANGELOG →](./CHANGELOG.md#0418---2026-11-01)
|
|
26
30
|
> **What's new in 0.4.17** — **a dry-run dream pass no longer writes to the database** (audit 2026-10-25). `loop-memory dream` and `POST /api/v1/dream` have always documented themselves as *"nothing is written unless `apply=True`"* — but the pass's decay stage called `MemoryStore.rescore_all()` unconditionally, which rewrote the `memories.score` column of **every** row while claiming to only inspect the store. The expiry and merge decisions were correctly left unapplied; the score decay was not. `rescore_all()` now takes a `dry_run` flag: it computes every new score and reports how many rows *would* change but issues no `UPDATE`, and the dream pass runs it with `dry_run=not apply`. A dry run still reports the size of the decay job through `report.rescored` while touching nothing. The contract wording is now exact rather than aspirational — a dry run changes **no memory data** (no expiry, no supersession, no rescore); the single write it still performs is one `cognitive_audit` row per proposed action with `action="suggest"` instead of `"applied"`, because that row *is* the reviewable plan the dashboard shows before you commit to it (`record_audit=False` suppresses even that). Also fixed in this release: `publish.yml` now sends `make_latest` as a JSON **boolean** (`gh api -F`) instead of a JSON **string** — GitHub's release API ignores the string form, so the "mark as latest" step had been returning 200 and reporting success while promoting nothing, leaving `/releases/latest` pointing at v0.4.14 through the 0.4.15 and 0.4.16 ships. The step now also re-reads `/releases/latest` and fails the job when the tag does not match, instead of trusting the exit code. 3 new regression cases (`tests/test_dream_2026_10_25.py`); suite at **958 passed / 4 skipped**. [Full CHANGELOG →](./CHANGELOG.md#0417---2026-10-25)
|
|
27
31
|
> **What's new in 0.4.16** — **subject-key logical fact slots + `latest_only` recall + the "dream" sleep-time consolidation pass** (audit 2026-10-25, weekly ecosystem sync — Mem0 v2.2.1 "Dream", arXiv 2605.27366 "In Defense of Forgotten", Zep/Graphiti temporal edges, Letta memory blocks). New nullable `subject_key` column on `memories` names the *logical slot* a fact occupies (`"editor.preference"`); `upsert_memory(subject_key=...)` makes the new row the live version and soft-supersedes the previous one with a `superseded_by` pointer, so the version history survives and `recall_as_of()` can still answer "what did we believe last week?". The content-hash dedup path is now slot-aware — two different slots holding the same text stay two rows. `SCHEMA_VERSION` bumps 12 → 13 with an idempotent in-place migration. New `latest_only=False` flag on `recall()` / `recall_hybrid()` (Mem0 v2 `SearchMemoryOptions.latest_only` parity) collapses the answer in three layers: rows outside their validity window at *now* are dropped, a subject-key slot resolves to its newest **live** row (looked up in the store, so a stale version never comes back just because it matched the query better), and near-duplicate texts cluster by token Jaccard (default 0.85) with the newest row winning. Wired through `GET /api/recall?latest_only=1`, `GET /api/v1/recall?latest_only=1`, `loop-memory recall --latest-only` and `MemoryClient.recall(latest_only=True)`. New `loop_memory/jobs/dream.py` implements the sleep-time consolidation Mem0 v2 calls the "Dream" — deterministic, zero-dep and **non-destructive**: it decays scores, then judges every row past its deadline on **utility** (`0.45 × importance + 0.30 × usage + 0.25 × recency`) so a frequently-recalled old fact is *protected* while a never-used stale row is **soft-expired** (`invalid_at` stamped, row kept, history kept) instead of deleted, and finally merges near-duplicates into a supersession chain. Run it with `loop-memory dream [--apply] [--json]` or `POST /api/v1/dream` — it is a **dry run by default** and every decision lands in `cognitive_audit` so the dashboard can show and revert it. New store primitives `expire_memory()` / `revive_memory()`, and `memory_stats()` now reports `subject_key`, `ttl`, `valid_at`, `invalid_at` and a derived `ttl_state`. **Also fixed a real defect**: the hybrid recall path (BM25 / LIKE / semantic channels) did not filter `superseded_by`, so `recall_hybrid()` — the production path behind the CLI, MCP server and dashboard — returned both versions of a superseded fact. Source: Mem0 v2.2.1 Dream + ADD-only architecture + arXiv 2605.27366 utility-aware eviction + Zep/Graphiti bi-temporal edges (pattern only, no code copied). 77 new regression cases. [Full CHANGELOG →](./CHANGELOG.md#0416---2026-10-25)
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "loop-memory-default",
|
|
3
|
+
"revision": 2,
|
|
4
|
+
"description": "Deterministic retrieval-quality fixture for Loop Memory. 30 memories + 12 wiki pages, 11 judged queries. The corpus is deliberately knowledge-graph-heavy so the trigram '知识图' occurs in 18/30 memories (60%), which pushes SQLite's bm25() inverse document frequency below zero and onto its 1e-6 floor -- the defect this benchmark exists to measure. Query q10 ('知识图谱') has no rare term at all, so every one of its terms is clamped and the whole channel score collapses; the report shows the raw FTS5 score alongside the repaired one. Most other queries pair one document that contains the query verbatim with a shorter near miss, so the exact-match rule is measurable too.",
|
|
5
|
+
"corpus": [
|
|
6
|
+
{
|
|
7
|
+
"id": "kg-01",
|
|
8
|
+
"kind": "fact",
|
|
9
|
+
"importance": 0.5,
|
|
10
|
+
"text": "知识图谱的整体架构由存储层、抽取层和查询层三个部分组成"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"id": "kg-02",
|
|
14
|
+
"kind": "fact",
|
|
15
|
+
"importance": 0.5,
|
|
16
|
+
"text": "知识图谱的存储层使用图数据库保存实体和关系并支持按租户分区隔离,同时提供冷热分层和定期归档能力以控制存储成本"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "kg-03",
|
|
20
|
+
"kind": "fact",
|
|
21
|
+
"importance": 0.5,
|
|
22
|
+
"text": "知识图谱的抽取层负责从文档中识别实体和属性并写入草稿库"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "kg-04",
|
|
26
|
+
"kind": "fact",
|
|
27
|
+
"importance": 0.5,
|
|
28
|
+
"text": "知识图谱的查询层同时提供自然语言接口和图遍历接口两种风格"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"id": "kg-05",
|
|
32
|
+
"kind": "fact",
|
|
33
|
+
"importance": 0.5,
|
|
34
|
+
"text": "知识图谱的实体消歧依赖上下文窗口并且最终需要人工复核确认"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"id": "kg-06",
|
|
38
|
+
"kind": "fact",
|
|
39
|
+
"importance": 0.5,
|
|
40
|
+
"text": "知识图谱的关系抽取分为显式关系和隐含关系两大类别来处理"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "kg-07",
|
|
44
|
+
"kind": "fact",
|
|
45
|
+
"importance": 0.5,
|
|
46
|
+
"text": "知识图谱的增量更新由定时任务和事件触发两种模式共同驱动"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"id": "kg-08",
|
|
50
|
+
"kind": "fact",
|
|
51
|
+
"importance": 0.5,
|
|
52
|
+
"text": "知识图谱的质量评估覆盖完整性和一致性两个维度来衡量"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"id": "kg-09",
|
|
56
|
+
"kind": "fact",
|
|
57
|
+
"importance": 0.5,
|
|
58
|
+
"text": "知识图谱的可视化界面支持力导向图和时间轴视图两种呈现方式"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"id": "kg-10",
|
|
62
|
+
"kind": "fact",
|
|
63
|
+
"importance": 0.5,
|
|
64
|
+
"text": "知识图谱的权限模型区分全局空间和项目空间两套隔离策略"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"id": "kg-11",
|
|
68
|
+
"kind": "fact",
|
|
69
|
+
"importance": 0.5,
|
|
70
|
+
"text": "知识图谱的版本控制会记录每次抽取的输入快照和输出差异明细"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"id": "kg-12",
|
|
74
|
+
"kind": "fact",
|
|
75
|
+
"importance": 0.5,
|
|
76
|
+
"text": "知识图谱的导入流程支持 JSONL 和 CSV 两种常见的文件格式"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"id": "kg-13",
|
|
80
|
+
"kind": "fact",
|
|
81
|
+
"importance": 0.5,
|
|
82
|
+
"text": "知识图谱的导出流程会把实体、关系和来源标识一并写出到磁盘"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"id": "kg-14",
|
|
86
|
+
"kind": "fact",
|
|
87
|
+
"importance": 0.5,
|
|
88
|
+
"text": "知识图谱的部署方式分为单机模式和容器模式两套基础设施方案"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"id": "kg-15",
|
|
92
|
+
"kind": "fact",
|
|
93
|
+
"importance": 0.6,
|
|
94
|
+
"text": "知识图谱构建流程的第一步是切分输入文档并保留原始偏移量用于后续的实体对齐工作,同时需要保留切分器的版本号以便回溯"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"id": "kg-16",
|
|
98
|
+
"kind": "fact",
|
|
99
|
+
"importance": 0.5,
|
|
100
|
+
"text": "构建流程的第一步是切分知识图谱的输入文档"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"id": "kg-17",
|
|
104
|
+
"kind": "fact",
|
|
105
|
+
"importance": 0.5,
|
|
106
|
+
"text": "知识图谱的构建流程包含切分和抽取两个阶段"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"id": "kg-18",
|
|
110
|
+
"kind": "fact",
|
|
111
|
+
"importance": 0.5,
|
|
112
|
+
"text": "知识图谱在存储层之上还有一层缓存用于加速热点查询"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"id": "kg-19",
|
|
116
|
+
"kind": "fact",
|
|
117
|
+
"importance": 0.6,
|
|
118
|
+
"text": "知识图谱术语表把实体、关系、属性和来源这四个概念放在同一份文档里对照说明,知识图谱的每个概念都要有明确定义和清晰的边界"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"id": "kg-20",
|
|
122
|
+
"kind": "fact",
|
|
123
|
+
"importance": 0.5,
|
|
124
|
+
"text": "知识图谱构建的准备工作包括确认数据源和配置抽取参数"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"id": "en-01",
|
|
128
|
+
"kind": "fact",
|
|
129
|
+
"importance": 0.6,
|
|
130
|
+
"text": "Kubernetes ingress controller runs in the platform namespace and watches every ingress object that is created inside the cluster so that routing rules stay in sync with the declared service definitions"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"id": "en-02",
|
|
134
|
+
"kind": "fact",
|
|
135
|
+
"importance": 0.5,
|
|
136
|
+
"text": "The ingress controller for kubernetes was migrated to a new namespace"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"id": "en-03",
|
|
140
|
+
"kind": "fact",
|
|
141
|
+
"importance": 0.6,
|
|
142
|
+
"text": "The blue green rollout switches production traffic between two clusters and keeps the old one warm so that a rollback stays a single configuration change instead of a rebuild"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"id": "en-04",
|
|
146
|
+
"kind": "fact",
|
|
147
|
+
"importance": 0.5,
|
|
148
|
+
"text": "Rolling upgrades were replaced by a blue-green style rollout"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"id": "en-05",
|
|
152
|
+
"kind": "fact",
|
|
153
|
+
"importance": 0.5,
|
|
154
|
+
"text": "The operator rotates the tls certificate manually every ninety days"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"id": "en-08",
|
|
158
|
+
"kind": "fact",
|
|
159
|
+
"importance": 0.6,
|
|
160
|
+
"text": "Rotate TLS certificate without any downtime by scheduling the sidecar renewal job ahead of expiry so that the new chain is presented before the old one is revoked"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"id": "en-09",
|
|
164
|
+
"kind": "fact",
|
|
165
|
+
"importance": 0.6,
|
|
166
|
+
"text": "The recall budget caps how many characters a single injected context block may occupy before truncation removes the tail of the payload and the remaining slots are handed to the next ranked memory"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"id": "en-10",
|
|
170
|
+
"kind": "fact",
|
|
171
|
+
"importance": 0.5,
|
|
172
|
+
"text": "Budget enforcement happens during the merge step, so recall limits apply equally to the fast path and to the slower fallback path"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"id": "en-11",
|
|
176
|
+
"kind": "fact",
|
|
177
|
+
"importance": 0.6,
|
|
178
|
+
"text": "Knowledge graph ingestion runs as a background job that batches documents and writes entities into the graph store within the configured window and reports how many records were skipped"
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"id": "en-12",
|
|
182
|
+
"kind": "fact",
|
|
183
|
+
"importance": 0.5,
|
|
184
|
+
"text": "Ingestion of the graph pipeline is scheduled nightly and refreshed whenever the source corpus changes"
|
|
185
|
+
}
|
|
186
|
+
],
|
|
187
|
+
"wiki": [
|
|
188
|
+
{
|
|
189
|
+
"slug": "deploy-runbook",
|
|
190
|
+
"title": "部署流程",
|
|
191
|
+
"importance": 0.6,
|
|
192
|
+
"body": "部署流程分为预发验证、灰度发布和全量发布三个阶段,每个阶段都要值班同学确认指标符合预期之后才能继续推进下一步的工作安排,遇到异常时按回滚预案处理"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"slug": "rollback-guide",
|
|
196
|
+
"title": "回滚指引",
|
|
197
|
+
"importance": 0.5,
|
|
198
|
+
"body": "回滚的操作步骤与正向流程类似但需要先冻结变更窗口并在值班表上记录"
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"slug": "incident-review",
|
|
202
|
+
"title": "故障复盘",
|
|
203
|
+
"importance": 0.5,
|
|
204
|
+
"body": "故障复盘包括时间线重建、根因定位和改进行动项三个部分"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"slug": "cert-rotation",
|
|
208
|
+
"title": "证书轮换",
|
|
209
|
+
"importance": 0.5,
|
|
210
|
+
"body": "证书轮换由 sidecar 代理每九十天自动执行一次并记录审计日志"
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"slug": "kg-glossary",
|
|
214
|
+
"title": "知识图谱术语表",
|
|
215
|
+
"importance": 0.5,
|
|
216
|
+
"body": "本术语表统一了实体、关系、属性和来源四个核心概念的叫法"
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"slug": "onboarding",
|
|
220
|
+
"title": "新人上手",
|
|
221
|
+
"importance": 0.5,
|
|
222
|
+
"body": "新人上手流程分为环境准备、阅读规范和第一个改动三步"
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"slug": "nda-signing",
|
|
226
|
+
"title": "合同签署流程",
|
|
227
|
+
"importance": 0.5,
|
|
228
|
+
"body": "签署流程需要法务和业务双方在系统中确认后才会正式生效"
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"slug": "deploy-signoff",
|
|
232
|
+
"title": "上线签署流程",
|
|
233
|
+
"importance": 0.5,
|
|
234
|
+
"body": "上线签署流程由发布负责人和值班同学共同完成,之后才进入部署流水线等待执行"
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"slug": "rotation-policy",
|
|
238
|
+
"title": "轮换策略",
|
|
239
|
+
"importance": 0.5,
|
|
240
|
+
"body": "策略规定书轮换窗口每月开放一次,实际执行由平台自动化任务完成"
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
"slug": "cert-exceptions",
|
|
244
|
+
"title": "证书例外",
|
|
245
|
+
"importance": 0.5,
|
|
246
|
+
"body": "证书轮换策略的例外情况需要人工审批并记录原因"
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"slug": "release-notes",
|
|
250
|
+
"title": "发布说明",
|
|
251
|
+
"importance": 0.6,
|
|
252
|
+
"body": "发布流程要求每次上线都要附上变更说明和影响范围,回滚流程与部署流程的触发条件不同需要分别评估,缺少变更说明的发布一律不予放行"
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
"slug": "release-pipeline",
|
|
256
|
+
"title": "发布流水线",
|
|
257
|
+
"importance": 0.5,
|
|
258
|
+
"body": "布流程的每一步都要有回滚预案,发布流水线在蓝绿切换中会先验证新环境的健康状态再切流量"
|
|
259
|
+
}
|
|
260
|
+
],
|
|
261
|
+
"queries": [
|
|
262
|
+
{
|
|
263
|
+
"id": "q1",
|
|
264
|
+
"query": "知识图谱构建流程",
|
|
265
|
+
"channel": "memories",
|
|
266
|
+
"relevant": [
|
|
267
|
+
"kg-15"
|
|
268
|
+
],
|
|
269
|
+
"note": "The authoritative step list contains the query verbatim; kg-16 and kg-17 match only the boundary trigrams. All three have the same term frequencies, so the pre-0.4.18 tie-break was document length, which demoted the complete note."
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
"id": "q2",
|
|
273
|
+
"query": "知识图谱的存储层",
|
|
274
|
+
"channel": "memories",
|
|
275
|
+
"relevant": [
|
|
276
|
+
"kg-02"
|
|
277
|
+
],
|
|
278
|
+
"note": "kg-02 holds the query verbatim and is the longest of the three candidates; kg-01 and kg-18 are near misses."
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
"id": "q3",
|
|
282
|
+
"query": "kubernetes ingress controller",
|
|
283
|
+
"channel": "memories",
|
|
284
|
+
"relevant": [
|
|
285
|
+
"en-01"
|
|
286
|
+
],
|
|
287
|
+
"note": "en-01 holds the query verbatim; en-02 mentions the same three terms in a different order."
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
"id": "q4",
|
|
291
|
+
"query": "blue green rollout",
|
|
292
|
+
"channel": "memories",
|
|
293
|
+
"relevant": [
|
|
294
|
+
"en-03"
|
|
295
|
+
],
|
|
296
|
+
"note": "en-03 holds the query verbatim; en-04 uses a hyphenated form."
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
"id": "q5",
|
|
300
|
+
"query": "rotate tls certificate",
|
|
301
|
+
"channel": "memories",
|
|
302
|
+
"relevant": [
|
|
303
|
+
"en-08"
|
|
304
|
+
],
|
|
305
|
+
"note": "en-08 holds the query verbatim; en-05 uses the verb form 'rotates'."
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
"id": "q6",
|
|
309
|
+
"query": "recall budget",
|
|
310
|
+
"channel": "memories",
|
|
311
|
+
"relevant": [
|
|
312
|
+
"en-09"
|
|
313
|
+
],
|
|
314
|
+
"note": "en-09 holds the query verbatim; en-10 mentions both terms separately."
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
"id": "q7",
|
|
318
|
+
"query": "knowledge graph ingestion",
|
|
319
|
+
"channel": "memories",
|
|
320
|
+
"relevant": [
|
|
321
|
+
"en-11"
|
|
322
|
+
],
|
|
323
|
+
"note": "en-11 holds the query verbatim; en-12 mentions both terms separately."
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
"id": "q8",
|
|
327
|
+
"query": "部署流程",
|
|
328
|
+
"channel": "wiki",
|
|
329
|
+
"relevant": [
|
|
330
|
+
"deploy-runbook"
|
|
331
|
+
],
|
|
332
|
+
"note": "deploy-runbook holds the query verbatim; deploy-signoff matches the boundary trigrams '部署流' and '署流程' without the phrase."
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
"id": "q9",
|
|
336
|
+
"query": "发布流程",
|
|
337
|
+
"channel": "wiki",
|
|
338
|
+
"relevant": [
|
|
339
|
+
"release-notes"
|
|
340
|
+
],
|
|
341
|
+
"note": "release-notes holds the query verbatim; release-pipeline matches the boundary trigrams '发布流' and '布流程' without the phrase."
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
"id": "q10",
|
|
345
|
+
"query": "知识图谱",
|
|
346
|
+
"channel": "memories",
|
|
347
|
+
"relevant": [
|
|
348
|
+
"kg-19"
|
|
349
|
+
],
|
|
350
|
+
"note": "Every term of this query is common, so the raw FTS5 score collapses onto its 1e-6 idf floor. The repaired score keeps a usable scale and the verbatim match still ranks first."
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
"id": "q11",
|
|
354
|
+
"query": "知识图谱构建",
|
|
355
|
+
"channel": "memories",
|
|
356
|
+
"relevant": [
|
|
357
|
+
"kg-15",
|
|
358
|
+
"kg-20"
|
|
359
|
+
],
|
|
360
|
+
"note": "Two documents are genuinely relevant and both are retrievable: the canonical step list and the preparation checklist. recall@1 therefore cannot reach 1.0 for this query, so the metric keeps headroom. kg-16 stays in the corpus as a distractor: it discusses the same first step but the trigram candidate query cannot see it, because it needs CJK word segmentation to match '知识图谱构建'."
|
|
361
|
+
}
|
|
362
|
+
]
|
|
363
|
+
}
|
|
@@ -134,8 +134,21 @@ class BenchmarkReport:
|
|
|
134
134
|
|
|
135
135
|
|
|
136
136
|
def default_fixture_path() -> Path:
|
|
137
|
-
"""Absolute path of the fixture shipped with the package.
|
|
138
|
-
|
|
137
|
+
"""Absolute path of the fixture shipped with the package.
|
|
138
|
+
|
|
139
|
+
Audit 2026-11-01 (loop-memory 0.4.19): the fixture is *package
|
|
140
|
+
data*. It was not declared in ``pyproject.toml``, so the 0.4.18
|
|
141
|
+
wheel shipped without it and ``loop-memory bench`` died with a
|
|
142
|
+
``FileNotFoundError`` on every PyPI install. ``importlib.resources``
|
|
143
|
+
is the fallback for installs whose files are not reachable through
|
|
144
|
+
``__file__``.
|
|
145
|
+
"""
|
|
146
|
+
direct = Path(__file__).resolve().parent / "fixtures" / "default.json"
|
|
147
|
+
if direct.is_file():
|
|
148
|
+
return direct
|
|
149
|
+
from importlib.resources import files
|
|
150
|
+
|
|
151
|
+
return Path(str(files("loop_memory.bench").joinpath("fixtures").joinpath("default.json")))
|
|
139
152
|
|
|
140
153
|
|
|
141
154
|
def load_fixture(path: str | Path | None = None) -> tuple[dict[str, Any], str]:
|
|
@@ -146,7 +159,16 @@ def load_fixture(path: str | Path | None = None) -> tuple[dict[str, Any], str]:
|
|
|
146
159
|
re-serialisation of it.
|
|
147
160
|
"""
|
|
148
161
|
p = Path(path).expanduser() if path else default_fixture_path()
|
|
149
|
-
|
|
162
|
+
try:
|
|
163
|
+
raw = p.read_bytes()
|
|
164
|
+
except FileNotFoundError as exc:
|
|
165
|
+
raise FileNotFoundError(
|
|
166
|
+
f"benchmark fixture not found: {p}\n"
|
|
167
|
+
"The default fixture ships as package data; if you installed "
|
|
168
|
+
"loop-memory from PyPI and this is the packaged fixture, the "
|
|
169
|
+
"distribution is missing it (that was the 0.4.18 bug fixed "
|
|
170
|
+
"in 0.4.19). Pass --fixture PATH to point at your own."
|
|
171
|
+
) from exc
|
|
150
172
|
digest = hashlib.sha256(raw).hexdigest()
|
|
151
173
|
data = json.loads(raw.decode("utf-8"))
|
|
152
174
|
if not isinstance(data, dict):
|
|
@@ -85,7 +85,11 @@ def run_bench(args: list) -> int:
|
|
|
85
85
|
|
|
86
86
|
from ...bench.runner import compare_reports, load_fixture, run_benchmark
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
try:
|
|
89
|
+
fixture, digest = load_fixture(ns.fixture)
|
|
90
|
+
except FileNotFoundError as exc:
|
|
91
|
+
print(str(exc), file=sys.stderr)
|
|
92
|
+
return 2
|
|
89
93
|
report = run_benchmark(
|
|
90
94
|
fixture, fixture_sha256=digest, top_k=ns.top_k
|
|
91
95
|
).to_dict()
|