memorymaster 3.17.0__tar.gz → 3.18.0__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.
- {memorymaster-3.17.0/memorymaster.egg-info → memorymaster-3.18.0}/PKG-INFO +1 -1
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/config.py +61 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/retrieval.py +45 -7
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/service.py +4 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/steward.py +22 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0/memorymaster.egg-info}/PKG-INFO +1 -1
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster.egg-info/SOURCES.txt +2 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/pyproject.toml +1 -1
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/bench_longmemeval.py +66 -5
- memorymaster-3.18.0/tests/test_retrieval_profiles.py +142 -0
- memorymaster-3.18.0/tests/test_steward_daydream_hook.py +116 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/LICENSE +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/README.md +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/artifacts/bm25-per-field-eval-harness.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/benchmarks/longmemeval_runner.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/benchmarks/longmemeval_vector_runner.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/benchmarks/perf_smoke.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/__init__.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/__main__.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/_storage_lifecycle.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/_storage_read.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/_storage_schema.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/_storage_shared.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/_storage_sources.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/_storage_write_claims.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/access_control.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/action_exporters.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/action_extractor.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/atlas_claim_extractor.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/atlas_contract.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/auto_extractor.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/auto_resolver.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/candidate_dedupe.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/claim_edges.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/claim_verifier.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/cli.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/cli_handlers_basic.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/cli_handlers_curation.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/cli_helpers.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/closets.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/config_templates/claude-md-append.md +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/config_templates/codex-agents-md-append.md +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/config_templates/hooks/memorymaster-auto-ingest.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/config_templates/hooks/memorymaster-classify.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/config_templates/hooks/memorymaster-dream-sync.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/config_templates/hooks/memorymaster-precompact.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/config_templates/hooks/memorymaster-recall.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/config_templates/hooks/memorymaster-session-start.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/config_templates/hooks/memorymaster-steward-cycle.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/config_templates/hooks/memorymaster-validate-wiki.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/conflict_resolver.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/connectors/__init__.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/connectors/whatsapp.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/context_hook.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/context_optimizer.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/daily_notes.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/dashboard.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/db_merge.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/dream_bridge.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/embeddings.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/entity_extractor.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/entity_graph.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/entity_registry.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/federated_graphify.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/feedback.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/graph_store.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/hook_log.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/jobs/__init__.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/jobs/calibration.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/jobs/compact_summaries.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/jobs/compactor.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/jobs/daydream_ingest.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/jobs/decay.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/jobs/dedup.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/jobs/deterministic.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/jobs/entity_graph_export.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/jobs/extractor.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/jobs/staleness.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/jobs/validator.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/key_rotator.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/lifecycle.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/llm_provider.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/llm_rerank.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/llm_steward.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/mcp_server.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/mcp_usage.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/media_processing.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/media_providers.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/metrics_exporter.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/models.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/observability.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/operator.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/operator_queue.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/plugins.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/policy.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/postgres_store.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/qdrant_backend.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/qdrant_recall_fallback.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/qmd_bridge.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/query_classifier.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/query_expansion.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/recall_fusion.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/recall_tokenizer.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/retry.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/review.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/rl_trainer.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/scheduler.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/schema.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/schema.sql +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/schema_postgres.sql +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/scope_utils.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/security.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/session_tracker.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/setup_hooks.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/skill_evolver.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/snapshot.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/steward_classifier.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/steward_features.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/storage.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/store_factory.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/transcript_miner.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/turn_schema.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/vault_bases.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/vault_curator.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/vault_exporter.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/vault_linter.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/vault_log.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/vault_query_capture.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/vault_synthesis.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/verbatim_recall.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/verbatim_store.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/webhook.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/wiki_engine.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/wiki_freshness.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/wiki_similarity.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/wiki_suggest.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster/wiki_validate.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster.egg-info/dependency_links.txt +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster.egg-info/entry_points.txt +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster.egg-info/requires.txt +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/memorymaster.egg-info/top_level.txt +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/agg_recall_latency.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/alert_operator_metrics.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/audit_dedupe_precision.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/autoresearch_daemon.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/backfill_entity_extraction.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/backfill_graph_store.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/backfill_stop_hook_citations.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/backtest_steward_classifier.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/build_steward_training_set.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/check_hook_template_drift.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/claude_to_turns.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/codex_live_to_turns.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/compaction_edge_cases.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/compaction_trace_report.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/compaction_trace_validate.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/confusion_matrix_eval.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/conversation_importer.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/conversation_to_turns.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/e2e_operator.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/email_live_to_turns.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/eval_bm25_sweep.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/eval_classify_f1.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/eval_memorymaster.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/eval_recall_precision_at_5.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/eval_recall_quality.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/eval_steward_pareto.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/eval_verbatim_recall.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/expand_recall_eval.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/generate_drill_signoff.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/git_to_turns.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/github_live_to_turns.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/gitnexus_to_claims.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/grid_recall_weights.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/index_claims_to_qdrant.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/ingest_planning_docs.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/jira_live_to_turns.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/label_prompts_with_judge.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/llm_benchmark.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/measure_dedupe_thresholds.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/merge_scope_variants.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/messages_to_turns.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/operator_metrics.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/precompute_candidates.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/recurring_incident_drill.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/release_readiness.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/run_codex_autologger.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/run_incident_drill.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/scheduled_ingest.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/setup-hooks.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/slack_live_to_turns.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/sync_hook_templates.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/tickets_to_turns.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/train_steward_classifier.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/scripts/webhook_to_turns.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/setup.cfg +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/conftest.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/integration/test_extract_llm_ollama_live.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_access_control.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_action_exporters.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_action_extractor.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_atlas_claim_extractor.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_atlas_contract.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_atlas_source_schema.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_auto_extractor.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_auto_ingest_hook_citations.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_auto_ingest_hook_schema.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_auto_resolver.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_auto_validate.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_bm25_per_field.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_calibration.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_calibration_priors_applied.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_candidate_dedupe.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_claim_edges.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_claim_links.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_claim_type_ranking.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_classify_hook_f1.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_classify_hook_latency.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_claude_to_turns.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_cli_dry_run.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_cli_json_flag.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_cli_ready.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_cli_review_queue.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_cli_subcommands.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_closets.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_closets_recall_integration.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_compact_summaries.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_compact_summaries_sensitivity.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_compaction_trace.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_compactor_artifact_order.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_config.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_conflict_resolver.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_confusion_matrix_eval.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_connection_retry.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_connectors.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_context_hook.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_context_optimizer.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_context_optimizer_provider.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_conversation_to_turns.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_dashboard.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_dashboard_coverage.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_dashboard_latency.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_dashboard_lineage.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_dashboard_review_queue.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_daydream_ingest.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_db_merge_confidence_conflict.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_db_merge_coverage_v2.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_decay_coverage.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_decay_respects_pinned.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_dedup.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_dedup_cli.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_dedup_conflict_disambiguation.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_deterministic_predicates.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_dream_bridge_coverage_v2.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_dream_bridge_sensitivity.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_embeddings_coverage.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_entity_extractor.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_entity_extractor_llm.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_entity_graph.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_entity_graph_export.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_entity_new_kinds.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_entity_regex_v3.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_entity_registry.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_eval_harness.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_events_schema.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_extract_llm_ollama.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_federated_graphify_mcp.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_federated_query_safety.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_feedback.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_fts5_search.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_graph_distance.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_graph_store.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_handler_regressions.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_hook_env_isolation.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_human_id.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_incident_drill_runner.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_integration_workflows.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_key_rotator.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_lifecycle.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_lifecycle_supersede_invariant.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_llm_fallback.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_llm_provider_claude_cli.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_llm_provider_key_rotation.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_llm_steward_coverage.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_llm_steward_key_rotation.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_mcp_filter_bypass.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_mcp_helpers.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_mcp_rate_limit.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_mcp_server_validation.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_mcp_usage.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_media_processing.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_meta_decisions.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_metrics_exporter.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_observability.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_obsidian_mind_patterns.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_operator.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_operator_queue.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_perf_smoke_config.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_plugins.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_policy_coverage.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_policy_mode_env.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_postgres_parity.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_qdrant_backend.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_qmd_bridge.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_query_classifier.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_query_expansion.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_recall_entity_fanout.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_recall_fusion.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_recall_latency.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_recall_precision_at_5.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_recall_tokenizer.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_recall_vector_fallback.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_reliability_hardening.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_resolvers_concurrent_supersede.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_retrieval_profile.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_retrieval_rrf_tiebreaker.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_retrieval_weights.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_review.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_rl_trainer.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_rrf_auto_gate.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_scheduler.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_schema.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_scope_boost.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_scope_utils.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_security_access.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_security_patterns.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_sensitivity_filter_adversarial.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_sensitivity_filter_adversarial_v2.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_sensitivity_filter_t07.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_service_coverage.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_session_tracker.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_snapshot.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_snapshot_roundtrip.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_sqlite_core.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_staleness.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_stealth_mode.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_steward.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_steward_classifier.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_steward_features.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_steward_features_v3.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_steward_resolution_parity.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_storage_parity.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_store_factory.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_tenant_isolation.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_turn_schema.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_two_pass_recall.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_v311_fixes.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_v313_e2e.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_v313_run_cycle_dedupe.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_v390_e2e.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_v391_strict_warnings.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_vault_exporter.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_vault_linter_orphan.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_vector_search.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_verbatim_dedup.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_verbatim_recall.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_verbatim_store.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_verbatim_store_qdrant.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_webhook.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_whatsapp_importer.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_wiki_autopromote.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_wiki_binding.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_wiki_engine_idempotency.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_wiki_explored_and_contradictions.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_wiki_freshness.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_wiki_similarity_multiscope.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_wiki_suggest.py +0 -0
- {memorymaster-3.17.0 → memorymaster-3.18.0}/tests/test_wiki_validate_cli.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memorymaster
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.18.0
|
|
4
4
|
Summary: Production-grade memory reliability system for AI coding agents. Lifecycle-managed claims with citations, conflict detection, steward governance, and MCP integration.
|
|
5
5
|
Author: wolverin0
|
|
6
6
|
License: MIT
|
|
@@ -67,6 +67,17 @@ MEMORYMASTER_RRF_TIEBREAKER_THRESHOLD
|
|
|
67
67
|
Maximum adjacent score gap considered a near tie.
|
|
68
68
|
Default: ``0.01``
|
|
69
69
|
|
|
70
|
+
MEMORYMASTER_RETRIEVAL_PROFILE_<TYPE>
|
|
71
|
+
Per-question-type retrieval weight override. ``<TYPE>`` is the
|
|
72
|
+
upper-snake-case form of the question type (LongMemEval-S labels:
|
|
73
|
+
SINGLE_SESSION_USER, SINGLE_SESSION_ASSISTANT, SINGLE_SESSION_PREFERENCE,
|
|
74
|
+
TEMPORAL_REASONING, KNOWLEDGE_UPDATE, MULTI_SESSION). Value format
|
|
75
|
+
matches ``MEMORYMASTER_RETRIEVAL_WEIGHTS``: comma-separated floats
|
|
76
|
+
``lex,conf,fresh,vec``. When set, ``Config.retrieval_profile(qtype)``
|
|
77
|
+
returns the profile and ``retrieval._compute_claim_score`` consumes it
|
|
78
|
+
instead of the default weights. Profiles are opt-in; missing types fall
|
|
79
|
+
back to the global ``cfg.retrieval_weights``.
|
|
80
|
+
|
|
70
81
|
MEMORYMASTER_CONFIG_FILE
|
|
71
82
|
Path to a JSON config file. Keys match attribute names on ``Config``.
|
|
72
83
|
"""
|
|
@@ -218,6 +229,15 @@ class Config:
|
|
|
218
229
|
default_factory=lambda: dict(INITIAL_CONFIDENCE_BY_TYPE)
|
|
219
230
|
)
|
|
220
231
|
|
|
232
|
+
# --- Per-question-type retrieval weight profiles (S3, opt-in) ---
|
|
233
|
+
# Maps the canonical question_type slug (lowercase, hyphens preserved,
|
|
234
|
+
# e.g. "single-session-preference") to a (W_LEX, W_CONF, W_FRESH, W_VEC)
|
|
235
|
+
# tuple. Populated by env vars MEMORYMASTER_RETRIEVAL_PROFILE_<TYPE>.
|
|
236
|
+
# Empty by default — retrieval falls back to ``retrieval_weights``.
|
|
237
|
+
retrieval_profiles: Dict[str, tuple[float, float, float, float]] = field(
|
|
238
|
+
default_factory=dict
|
|
239
|
+
)
|
|
240
|
+
|
|
221
241
|
# --- Derived convenience dicts ---
|
|
222
242
|
|
|
223
243
|
@property
|
|
@@ -270,6 +290,20 @@ class Config:
|
|
|
270
290
|
self.lexical_weight_prefix,
|
|
271
291
|
)
|
|
272
292
|
|
|
293
|
+
def retrieval_profile(
|
|
294
|
+
self, question_type: str | None
|
|
295
|
+
) -> tuple[float, float, float, float] | None:
|
|
296
|
+
"""Per-question-type weight override, or None to fall back to default.
|
|
297
|
+
|
|
298
|
+
Accepts the canonical bench label (e.g. ``single-session-preference``)
|
|
299
|
+
or the classifier output (e.g. ``preference``) — both lookup forms are
|
|
300
|
+
normalized to lowercase before lookup. Returning None signals the
|
|
301
|
+
caller to use ``retrieval_weights`` as usual.
|
|
302
|
+
"""
|
|
303
|
+
if not question_type:
|
|
304
|
+
return None
|
|
305
|
+
return self.retrieval_profiles.get(question_type.strip().lower())
|
|
306
|
+
|
|
273
307
|
|
|
274
308
|
# ---------------------------------------------------------------------------
|
|
275
309
|
# Module-level singleton
|
|
@@ -363,6 +397,7 @@ def load_config(config_path: str | Path | None = None) -> Config:
|
|
|
363
397
|
_apply_env_bool(overrides, "MEMORYMASTER_LLM_RERANK", "llm_rerank")
|
|
364
398
|
_apply_env_bool(overrides, "MEMORYMASTER_RRF_TIEBREAKER", "rrf_tiebreaker_enabled")
|
|
365
399
|
_apply_env_float(overrides, "MEMORYMASTER_RRF_TIEBREAKER_THRESHOLD", "rrf_tiebreaker_threshold")
|
|
400
|
+
_apply_env_retrieval_profiles(overrides)
|
|
366
401
|
|
|
367
402
|
# Filter to only valid Config fields
|
|
368
403
|
valid_fields = {f.name for f in Config.__dataclass_fields__.values()}
|
|
@@ -404,3 +439,29 @@ def _apply_env_bool(overrides: dict[str, object], env_var: str, key: str) -> Non
|
|
|
404
439
|
if not raw:
|
|
405
440
|
return
|
|
406
441
|
overrides[key] = raw in {"1", "true", "yes", "on"}
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
_PROFILE_PREFIX = "MEMORYMASTER_RETRIEVAL_PROFILE_"
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def _apply_env_retrieval_profiles(overrides: dict[str, object]) -> None:
|
|
448
|
+
"""Collect per-type profile overrides from env into a single dict.
|
|
449
|
+
|
|
450
|
+
Scans every env var starting with ``MEMORYMASTER_RETRIEVAL_PROFILE_``.
|
|
451
|
+
The suffix becomes the question_type slug (lowercased, underscores
|
|
452
|
+
converted to hyphens to match the LongMemEval-S canonical labels like
|
|
453
|
+
``single-session-preference``).
|
|
454
|
+
"""
|
|
455
|
+
profiles: dict[str, tuple[float, float, float, float]] = {}
|
|
456
|
+
for key, raw in os.environ.items():
|
|
457
|
+
if not key.startswith(_PROFILE_PREFIX):
|
|
458
|
+
continue
|
|
459
|
+
raw = raw.strip()
|
|
460
|
+
if not raw:
|
|
461
|
+
continue
|
|
462
|
+
suffix = key[len(_PROFILE_PREFIX):]
|
|
463
|
+
qtype = suffix.lower().replace("_", "-")
|
|
464
|
+
values = _parse_floats(raw, 4)
|
|
465
|
+
profiles[qtype] = (values[0], values[1], values[2], values[3])
|
|
466
|
+
if profiles:
|
|
467
|
+
overrides["retrieval_profiles"] = profiles
|
|
@@ -130,14 +130,42 @@ def rank_claims(
|
|
|
130
130
|
limit: int = 20,
|
|
131
131
|
vector_hook: VectorSearchHook | None = None,
|
|
132
132
|
semantic_vectors: bool = False,
|
|
133
|
+
query_type: str | None = None,
|
|
133
134
|
) -> list[Claim]:
|
|
134
|
-
return [
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
return [
|
|
136
|
+
row.claim
|
|
137
|
+
for row in rank_claim_rows(
|
|
138
|
+
query_text,
|
|
139
|
+
claims,
|
|
140
|
+
mode=mode,
|
|
141
|
+
limit=limit,
|
|
142
|
+
vector_hook=vector_hook,
|
|
143
|
+
semantic_vectors=semantic_vectors,
|
|
144
|
+
query_type=query_type,
|
|
145
|
+
)
|
|
146
|
+
]
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _compute_claim_score(
|
|
150
|
+
claim: Claim,
|
|
151
|
+
lexical: float,
|
|
152
|
+
confidence: float,
|
|
153
|
+
freshness: float,
|
|
154
|
+
vector: float,
|
|
155
|
+
vector_enabled: bool,
|
|
156
|
+
semantic_vectors: bool,
|
|
157
|
+
query_type: str | None = None,
|
|
158
|
+
) -> float:
|
|
159
|
+
"""Compute relevance score for a claim.
|
|
160
|
+
|
|
161
|
+
When ``query_type`` matches a configured per-type profile
|
|
162
|
+
(``cfg.retrieval_profile(query_type)``), the profile's 4-tuple replaces
|
|
163
|
+
``cfg.retrieval_weights`` for vector-enabled paths. The no-vector path
|
|
164
|
+
is unchanged — profiles only override the hybrid blend.
|
|
165
|
+
"""
|
|
139
166
|
cfg = get_config()
|
|
140
|
-
|
|
167
|
+
profile = cfg.retrieval_profile(query_type) if query_type else None
|
|
168
|
+
w_l, w_c, w_f, w_v = profile if profile is not None else cfg.retrieval_weights
|
|
141
169
|
if vector_enabled and semantic_vectors:
|
|
142
170
|
# Real semantic embeddings use the same configurable blend as other
|
|
143
171
|
# vector-enabled ranking so env sweeps affect both paths.
|
|
@@ -252,6 +280,7 @@ def rank_claim_rows(
|
|
|
252
280
|
limit: int = 20,
|
|
253
281
|
vector_hook: VectorSearchHook | None = None,
|
|
254
282
|
semantic_vectors: bool = False,
|
|
283
|
+
query_type: str | None = None,
|
|
255
284
|
) -> list[RankedClaim]:
|
|
256
285
|
if mode not in RETRIEVAL_MODES:
|
|
257
286
|
raise ValueError(f"Unknown retrieval mode: {mode}")
|
|
@@ -289,7 +318,16 @@ def rank_claim_rows(
|
|
|
289
318
|
freshness = _freshness_score(claim)
|
|
290
319
|
vector = max(0.0, min(1.0, float(vector_scores.get(claim.id, 0.0))))
|
|
291
320
|
|
|
292
|
-
score = _compute_claim_score(
|
|
321
|
+
score = _compute_claim_score(
|
|
322
|
+
claim,
|
|
323
|
+
lexical,
|
|
324
|
+
confidence,
|
|
325
|
+
freshness,
|
|
326
|
+
vector,
|
|
327
|
+
vector_enabled,
|
|
328
|
+
semantic_vectors,
|
|
329
|
+
query_type=query_type,
|
|
330
|
+
)
|
|
293
331
|
|
|
294
332
|
ranked.append(
|
|
295
333
|
RankedClaim(
|
|
@@ -426,6 +426,7 @@ class MemoryService:
|
|
|
426
426
|
retrieval_profile: str | None = None,
|
|
427
427
|
allow_sensitive: bool = False,
|
|
428
428
|
scope_allowlist: list[str] | None = None,
|
|
429
|
+
query_type: str | None = None,
|
|
429
430
|
) -> list[Claim]:
|
|
430
431
|
rows = self.query_rows(
|
|
431
432
|
query_text=query_text,
|
|
@@ -438,6 +439,7 @@ class MemoryService:
|
|
|
438
439
|
retrieval_profile=retrieval_profile,
|
|
439
440
|
allow_sensitive=allow_sensitive,
|
|
440
441
|
scope_allowlist=scope_allowlist,
|
|
442
|
+
query_type=query_type,
|
|
441
443
|
)
|
|
442
444
|
return [row["claim"] for row in rows]
|
|
443
445
|
|
|
@@ -552,6 +554,7 @@ class MemoryService:
|
|
|
552
554
|
scope_allowlist: list[str] | None = None,
|
|
553
555
|
enrich_with_entities: bool = False,
|
|
554
556
|
requesting_agent: str | None = None,
|
|
557
|
+
query_type: str | None = None,
|
|
555
558
|
) -> list[dict[str, Any]]:
|
|
556
559
|
if limit <= 0:
|
|
557
560
|
return []
|
|
@@ -603,6 +606,7 @@ class MemoryService:
|
|
|
603
606
|
limit=rank_limit,
|
|
604
607
|
vector_hook=vector_hook,
|
|
605
608
|
semantic_vectors=semantic,
|
|
609
|
+
query_type=query_type,
|
|
606
610
|
)
|
|
607
611
|
ranked_rows = _rerank_with_profile(
|
|
608
612
|
ranked_rows,
|
|
@@ -1625,5 +1625,27 @@ def run_steward(
|
|
|
1625
1625
|
},
|
|
1626
1626
|
"artifact_path": str(Path(artifact_path)),
|
|
1627
1627
|
}
|
|
1628
|
+
import os
|
|
1629
|
+
|
|
1630
|
+
daydream_dir = os.environ.get("MEMORYMASTER_DAYDREAM_INGEST_DIR")
|
|
1631
|
+
if daydream_dir:
|
|
1632
|
+
try:
|
|
1633
|
+
import logging
|
|
1634
|
+
|
|
1635
|
+
from memorymaster.jobs.daydream_ingest import ingest_insights
|
|
1636
|
+
|
|
1637
|
+
verbose = os.environ.get("MEMORYMASTER_DAYDREAM_VERBOSE") == "1"
|
|
1638
|
+
path = Path(daydream_dir)
|
|
1639
|
+
if path.is_dir():
|
|
1640
|
+
summary = ingest_insights(service, path, dry_run=False)
|
|
1641
|
+
report["daydream"] = summary
|
|
1642
|
+
if verbose:
|
|
1643
|
+
logging.getLogger(__name__).info("daydream auto-ingest: %s", summary)
|
|
1644
|
+
else:
|
|
1645
|
+
report["daydream"] = {"skipped": "dir-not-found", "path": str(path)}
|
|
1646
|
+
except Exception as exc:
|
|
1647
|
+
report["daydream"] = {"error": str(exc)[:200]}
|
|
1648
|
+
if os.environ.get("MEMORYMASTER_DAYDREAM_VERBOSE") == "1":
|
|
1649
|
+
logging.getLogger(__name__).warning("daydream auto-ingest failed: %s", exc)
|
|
1628
1650
|
_write_artifact(Path(artifact_path), report)
|
|
1629
1651
|
return report
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memorymaster
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.18.0
|
|
4
4
|
Summary: Production-grade memory reliability system for AI coding agents. Lifecycle-managed claims with citations, conflict detection, steward governance, and MCP integration.
|
|
5
5
|
Author: wolverin0
|
|
6
6
|
License: MIT
|
|
@@ -308,6 +308,7 @@ tests/test_recall_vector_fallback.py
|
|
|
308
308
|
tests/test_reliability_hardening.py
|
|
309
309
|
tests/test_resolvers_concurrent_supersede.py
|
|
310
310
|
tests/test_retrieval_profile.py
|
|
311
|
+
tests/test_retrieval_profiles.py
|
|
311
312
|
tests/test_retrieval_rrf_tiebreaker.py
|
|
312
313
|
tests/test_retrieval_weights.py
|
|
313
314
|
tests/test_review.py
|
|
@@ -331,6 +332,7 @@ tests/test_staleness.py
|
|
|
331
332
|
tests/test_stealth_mode.py
|
|
332
333
|
tests/test_steward.py
|
|
333
334
|
tests/test_steward_classifier.py
|
|
335
|
+
tests/test_steward_daydream_hook.py
|
|
334
336
|
tests/test_steward_features.py
|
|
335
337
|
tests/test_steward_features_v3.py
|
|
336
338
|
tests/test_steward_resolution_parity.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "memorymaster"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.18.0"
|
|
8
8
|
description = "Production-grade memory reliability system for AI coding agents. Lifecycle-managed claims with citations, conflict detection, steward governance, and MCP integration."
|
|
9
9
|
license = {text = "MIT"}
|
|
10
10
|
authors = [{name = "wolverin0"}]
|
|
@@ -37,6 +37,7 @@ OPENAI_CHAT_URL = "https://api.openai.com/v1/chat/completions"
|
|
|
37
37
|
GEMINI_GENERATE_URL = "https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent"
|
|
38
38
|
OPENAI_JUDGE_MODEL = "gpt-4o"
|
|
39
39
|
GEMINI_FALLBACK_MODEL = "gemini-2.5-flash"
|
|
40
|
+
CLAUDE_CLI_JUDGE_MODEL = "claude-haiku-4-5-20251001"
|
|
40
41
|
DEFAULT_JUDGE = "sonnet"
|
|
41
42
|
DEFAULT_JUDGE_PACING_SECONDS = 1.0
|
|
42
43
|
DEFAULT_QA_MAX_SECONDS = 90 * 60
|
|
@@ -126,6 +127,8 @@ class JudgeClient:
|
|
|
126
127
|
if not self.openai_api_key:
|
|
127
128
|
raise RuntimeError("OPENAI_API_KEY is not set")
|
|
128
129
|
return call_openai_chat(prompt, self.openai_api_key, max_tokens=max_tokens, temperature=temperature)
|
|
130
|
+
if provider == "claude_cli":
|
|
131
|
+
return call_claude_cli_judge(prompt, max_tokens=max_tokens, temperature=temperature)
|
|
129
132
|
raise ValueError(f"Unknown judge provider: {provider}")
|
|
130
133
|
|
|
131
134
|
def _record(self, response: LLMResponse) -> None:
|
|
@@ -148,6 +151,8 @@ class JudgeClient:
|
|
|
148
151
|
return "gemini-2.5-flash"
|
|
149
152
|
if self.models_used == {ANTHROPIC_JUDGE_MODEL}:
|
|
150
153
|
return "sonnet"
|
|
154
|
+
if self.models_used == {CLAUDE_CLI_JUDGE_MODEL}:
|
|
155
|
+
return "claude_cli"
|
|
151
156
|
return "mixed"
|
|
152
157
|
|
|
153
158
|
@staticmethod
|
|
@@ -158,6 +163,10 @@ class JudgeClient:
|
|
|
158
163
|
return ["gpt-4o", "gemini", "sonnet"]
|
|
159
164
|
if primary == "gemini":
|
|
160
165
|
return ["gemini", "sonnet", "gpt-4o"]
|
|
166
|
+
if primary == "claude_cli":
|
|
167
|
+
# OAuth-only via local Claude Code binary; no API keys needed.
|
|
168
|
+
# Fall back to API providers only if explicit keys are present.
|
|
169
|
+
return ["claude_cli", "gpt-4o", "gemini", "sonnet"]
|
|
161
170
|
raise ValueError(f"Unknown judge: {primary}")
|
|
162
171
|
|
|
163
172
|
@staticmethod
|
|
@@ -166,6 +175,8 @@ class JudgeClient:
|
|
|
166
175
|
return ANTHROPIC_JUDGE_MODEL
|
|
167
176
|
if provider == "gemini":
|
|
168
177
|
return GEMINI_FALLBACK_MODEL
|
|
178
|
+
if provider == "claude_cli":
|
|
179
|
+
return CLAUDE_CLI_JUDGE_MODEL
|
|
169
180
|
return OPENAI_JUDGE_MODEL
|
|
170
181
|
|
|
171
182
|
|
|
@@ -256,7 +267,13 @@ def ingest_haystack(service: MemoryService, item: dict[str, Any], *, chunk_chars
|
|
|
256
267
|
)
|
|
257
268
|
|
|
258
269
|
|
|
259
|
-
def query_memory(
|
|
270
|
+
def query_memory(
|
|
271
|
+
service: MemoryService,
|
|
272
|
+
question: str,
|
|
273
|
+
top_k: int = 10,
|
|
274
|
+
*,
|
|
275
|
+
query_type: str | None = None,
|
|
276
|
+
) -> list[dict[str, Any]]:
|
|
260
277
|
service_limit = top_k if llm_rerank_available() else top_k * 3
|
|
261
278
|
return service.query_rows(
|
|
262
279
|
query_text=question,
|
|
@@ -265,6 +282,7 @@ def query_memory(service: MemoryService, question: str, top_k: int = 10) -> list
|
|
|
265
282
|
retrieval_mode="hybrid",
|
|
266
283
|
scope_allowlist=[BENCH_SCOPE],
|
|
267
284
|
allow_sensitive=True,
|
|
285
|
+
query_type=query_type,
|
|
268
286
|
)
|
|
269
287
|
|
|
270
288
|
|
|
@@ -370,7 +388,12 @@ def run_retrieval(
|
|
|
370
388
|
service = init_ephemeral_service(Path(tmp), embedding_provider=embedding_provider)
|
|
371
389
|
try:
|
|
372
390
|
ingest_haystack(service, item, chunk_chars=chunk_chars)
|
|
373
|
-
rows = query_memory(
|
|
391
|
+
rows = query_memory(
|
|
392
|
+
service,
|
|
393
|
+
str(item["question"]),
|
|
394
|
+
top_k=10,
|
|
395
|
+
query_type=str(item.get("question_type") or "") or None,
|
|
396
|
+
)
|
|
374
397
|
results.append(score_retrieval(item, rows))
|
|
375
398
|
finally:
|
|
376
399
|
del service
|
|
@@ -632,6 +655,43 @@ def call_anthropic_sonnet_requests(
|
|
|
632
655
|
)
|
|
633
656
|
|
|
634
657
|
|
|
658
|
+
def call_claude_cli_judge(
|
|
659
|
+
prompt: str,
|
|
660
|
+
*,
|
|
661
|
+
max_tokens: int,
|
|
662
|
+
temperature: float = 0.0,
|
|
663
|
+
) -> LLMResponse:
|
|
664
|
+
"""OAuth-via-CLI judge — wraps memorymaster.llm_provider._call_claude_cli.
|
|
665
|
+
|
|
666
|
+
Routes the judge prompt through the user's local `claude --print` binary so
|
|
667
|
+
the bench runs against a Claude Code subscription instead of api.anthropic.com.
|
|
668
|
+
No ANTHROPIC_API_KEY required. Per-call cold-start latency ~3-15s; budget
|
|
669
|
+
accordingly for full runs (~500 questions × 2 calls each).
|
|
670
|
+
|
|
671
|
+
`max_tokens` and `temperature` are accepted for signature parity with the
|
|
672
|
+
HTTP providers; the CLI does not honour them per-call (model defaults apply).
|
|
673
|
+
"""
|
|
674
|
+
# Local import — keeps the bench importable even if memorymaster is
|
|
675
|
+
# partially broken during dev. The provider helper handles binary discovery,
|
|
676
|
+
# timeouts, and Windows console suppression.
|
|
677
|
+
from memorymaster.llm_provider import _call_claude_cli
|
|
678
|
+
|
|
679
|
+
# Pin the model the bench reports via env, so judge_used_label stays stable.
|
|
680
|
+
os.environ.setdefault("MEMORYMASTER_LLM_MODEL", CLAUDE_CLI_JUDGE_MODEL)
|
|
681
|
+
text = _call_claude_cli(prompt, "").strip()
|
|
682
|
+
if not text:
|
|
683
|
+
raise RuntimeError(
|
|
684
|
+
"claude_cli judge returned empty output — check claude binary on PATH "
|
|
685
|
+
"and OAuth session validity"
|
|
686
|
+
)
|
|
687
|
+
return LLMResponse(
|
|
688
|
+
text=text,
|
|
689
|
+
model=CLAUDE_CLI_JUDGE_MODEL,
|
|
690
|
+
provider="claude_cli",
|
|
691
|
+
tokens=0,
|
|
692
|
+
)
|
|
693
|
+
|
|
694
|
+
|
|
635
695
|
def answer_question(question: str, contexts: list[str], judge: JudgeClient) -> str:
|
|
636
696
|
prompt = "\n\n".join(
|
|
637
697
|
[
|
|
@@ -669,7 +729,8 @@ def run_full(
|
|
|
669
729
|
anthropic_key = os.environ.get("ANTHROPIC_API_KEY", "").strip()
|
|
670
730
|
openai_key = os.environ.get("OPENAI_API_KEY", "").strip()
|
|
671
731
|
gemini_key = os.environ.get("GEMINI_API_KEY", "").strip()
|
|
672
|
-
|
|
732
|
+
# claude_cli judge runs entirely via local OAuth — no API keys required.
|
|
733
|
+
if judge_name != "claude_cli" and not anthropic_key and not openai_key and not gemini_key:
|
|
673
734
|
print("[full] no ANTHROPIC_API_KEY, OPENAI_API_KEY, or GEMINI_API_KEY; deferring QA")
|
|
674
735
|
return {
|
|
675
736
|
"mode": mode,
|
|
@@ -849,9 +910,9 @@ def parse_args() -> argparse.Namespace:
|
|
|
849
910
|
parser.add_argument("--qa-output", type=Path, default=QA_OUTPUT, help="QA-only output JSON path")
|
|
850
911
|
parser.add_argument(
|
|
851
912
|
"--judge",
|
|
852
|
-
choices=["sonnet", "gpt-4o", "gemini"],
|
|
913
|
+
choices=["sonnet", "gpt-4o", "gemini", "claude_cli"],
|
|
853
914
|
default=DEFAULT_JUDGE,
|
|
854
|
-
help="Primary judge model; default sonnet",
|
|
915
|
+
help="Primary judge model; default sonnet. claude_cli uses local OAuth (no API key)",
|
|
855
916
|
)
|
|
856
917
|
parser.add_argument(
|
|
857
918
|
"--judge-pacing-seconds",
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""S3 regression tests — per-question-type retrieval weight profiles.
|
|
2
|
+
|
|
3
|
+
Verifies:
|
|
4
|
+
1. With NO profile env var set, query_type is a no-op (back-compat).
|
|
5
|
+
2. With a per-type profile env var set, _compute_claim_score swaps the
|
|
6
|
+
weight tuple for queries of that type.
|
|
7
|
+
3. query_type that does NOT match any configured profile falls back to
|
|
8
|
+
the global retrieval_weights (default behaviour preserved).
|
|
9
|
+
4. The env-var slug mapping (UPPER_SNAKE -> lower-hyphen) round-trips
|
|
10
|
+
correctly so LongMemEval-S labels like 'single-session-preference'
|
|
11
|
+
are reachable via MEMORYMASTER_RETRIEVAL_PROFILE_SINGLE_SESSION_PREFERENCE.
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import pytest
|
|
16
|
+
|
|
17
|
+
from memorymaster.config import reset_config, get_config
|
|
18
|
+
from memorymaster.models import Claim
|
|
19
|
+
from memorymaster.retrieval import rank_claim_rows
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.fixture(autouse=True)
|
|
23
|
+
def _reset_config():
|
|
24
|
+
reset_config()
|
|
25
|
+
yield
|
|
26
|
+
reset_config()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _claim() -> Claim:
|
|
30
|
+
return Claim(
|
|
31
|
+
id=1,
|
|
32
|
+
text="alpha beta retrieval target",
|
|
33
|
+
idempotency_key=None,
|
|
34
|
+
normalized_text=None,
|
|
35
|
+
claim_type=None,
|
|
36
|
+
subject=None,
|
|
37
|
+
predicate=None,
|
|
38
|
+
object_value=None,
|
|
39
|
+
scope="project",
|
|
40
|
+
volatility="medium",
|
|
41
|
+
status="confirmed",
|
|
42
|
+
confidence=0.8,
|
|
43
|
+
pinned=False,
|
|
44
|
+
supersedes_claim_id=None,
|
|
45
|
+
replaced_by_claim_id=None,
|
|
46
|
+
created_at="2999-01-01T00:00:00+00:00",
|
|
47
|
+
updated_at="2999-01-01T00:00:00+00:00",
|
|
48
|
+
last_validated_at=None,
|
|
49
|
+
archived_at=None,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _rank(*, semantic_vectors: bool, query_type: str | None = None):
|
|
54
|
+
def vector_hook(query, claims):
|
|
55
|
+
return {c.id: 0.6 for c in claims}
|
|
56
|
+
|
|
57
|
+
return rank_claim_rows(
|
|
58
|
+
"alpha beta",
|
|
59
|
+
[_claim()],
|
|
60
|
+
mode="hybrid",
|
|
61
|
+
limit=1,
|
|
62
|
+
vector_hook=vector_hook,
|
|
63
|
+
semantic_vectors=semantic_vectors,
|
|
64
|
+
query_type=query_type,
|
|
65
|
+
)[0]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_query_type_without_profile_is_noop():
|
|
69
|
+
"""Passing query_type when no profile env var is set must not change
|
|
70
|
+
scores — back-compat with all existing callers."""
|
|
71
|
+
default = _rank(semantic_vectors=False)
|
|
72
|
+
typed = _rank(semantic_vectors=False, query_type="single-session-preference")
|
|
73
|
+
assert typed.score == pytest.approx(default.score)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_profile_env_var_overrides_default_weights(monkeypatch):
|
|
77
|
+
"""When a profile env var matches the query_type, weights swap.
|
|
78
|
+
|
|
79
|
+
Default vec-enabled weights: (lex=0.30, conf=0.20, fresh=0.10, vec=0.40).
|
|
80
|
+
Profile under test: (lex=0.10, conf=0.10, fresh=0.10, vec=0.70) — heavy
|
|
81
|
+
on vector, which is the predicted direction for ``single-session-preference``
|
|
82
|
+
where lexical matching is structurally weak (preferences are paraphrased).
|
|
83
|
+
"""
|
|
84
|
+
monkeypatch.setenv(
|
|
85
|
+
"MEMORYMASTER_RETRIEVAL_PROFILE_SINGLE_SESSION_PREFERENCE",
|
|
86
|
+
"0.10,0.10,0.10,0.70",
|
|
87
|
+
)
|
|
88
|
+
reset_config()
|
|
89
|
+
|
|
90
|
+
cfg = get_config()
|
|
91
|
+
assert cfg.retrieval_profile("single-session-preference") == (0.10, 0.10, 0.10, 0.70)
|
|
92
|
+
|
|
93
|
+
default = _rank(semantic_vectors=False)
|
|
94
|
+
typed = _rank(semantic_vectors=False, query_type="single-session-preference")
|
|
95
|
+
assert typed.score != default.score
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_profile_does_not_leak_to_other_query_types(monkeypatch):
|
|
99
|
+
"""Profile under one type must not affect queries with a different type
|
|
100
|
+
(or with no type at all)."""
|
|
101
|
+
monkeypatch.setenv(
|
|
102
|
+
"MEMORYMASTER_RETRIEVAL_PROFILE_SINGLE_SESSION_PREFERENCE",
|
|
103
|
+
"0.10,0.10,0.10,0.70",
|
|
104
|
+
)
|
|
105
|
+
reset_config()
|
|
106
|
+
|
|
107
|
+
default = _rank(semantic_vectors=False)
|
|
108
|
+
other_type = _rank(semantic_vectors=False, query_type="multi-session")
|
|
109
|
+
none_type = _rank(semantic_vectors=False, query_type=None)
|
|
110
|
+
|
|
111
|
+
assert other_type.score == pytest.approx(default.score)
|
|
112
|
+
assert none_type.score == pytest.approx(default.score)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_profile_slug_round_trips_underscores_to_hyphens(monkeypatch):
|
|
116
|
+
"""Env var SINGLE_SESSION_PREFERENCE must be reachable as the canonical
|
|
117
|
+
bench label 'single-session-preference'."""
|
|
118
|
+
monkeypatch.setenv(
|
|
119
|
+
"MEMORYMASTER_RETRIEVAL_PROFILE_TEMPORAL_REASONING",
|
|
120
|
+
"0.20,0.10,0.40,0.30",
|
|
121
|
+
)
|
|
122
|
+
reset_config()
|
|
123
|
+
|
|
124
|
+
cfg = get_config()
|
|
125
|
+
assert cfg.retrieval_profile("temporal-reasoning") == (0.20, 0.10, 0.40, 0.30)
|
|
126
|
+
# And the env-form lookup (uppercased, underscored) finds nothing — only
|
|
127
|
+
# the canonical lower-hyphen form is the key.
|
|
128
|
+
assert cfg.retrieval_profile("TEMPORAL_REASONING") is None
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_profile_works_in_semantic_path(monkeypatch):
|
|
132
|
+
"""The semantic_vectors=True branch must also honour the profile —
|
|
133
|
+
same blend formula, just different signal source."""
|
|
134
|
+
monkeypatch.setenv(
|
|
135
|
+
"MEMORYMASTER_RETRIEVAL_PROFILE_SINGLE_SESSION_PREFERENCE",
|
|
136
|
+
"0.10,0.10,0.10,0.70",
|
|
137
|
+
)
|
|
138
|
+
reset_config()
|
|
139
|
+
|
|
140
|
+
default = _rank(semantic_vectors=True)
|
|
141
|
+
typed = _rank(semantic_vectors=True, query_type="single-session-preference")
|
|
142
|
+
assert typed.score != default.score
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from memorymaster.service import MemoryService
|
|
9
|
+
from memorymaster.steward import run_steward
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pytest.fixture()
|
|
13
|
+
def service(tmp_path: Path) -> MemoryService:
|
|
14
|
+
svc = MemoryService(tmp_path / "memorymaster.db", workspace_root=tmp_path / "workspace")
|
|
15
|
+
svc.init_db()
|
|
16
|
+
return svc
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _run_clean_steward(service: MemoryService, tmp_path: Path) -> dict:
|
|
20
|
+
return run_steward(
|
|
21
|
+
service,
|
|
22
|
+
mode="manual",
|
|
23
|
+
max_cycles=1,
|
|
24
|
+
max_claims=10,
|
|
25
|
+
max_proposals=10,
|
|
26
|
+
max_probe_files=10,
|
|
27
|
+
artifact_path=tmp_path / "steward_report.json",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _write_insight(directory: Path) -> None:
|
|
32
|
+
directory.mkdir(parents=True, exist_ok=True)
|
|
33
|
+
(directory / "20260516-linking-patterns.md").write_text(
|
|
34
|
+
"""---
|
|
35
|
+
type: daydream
|
|
36
|
+
created_date: 2026-05-16
|
|
37
|
+
scores:
|
|
38
|
+
average: 8.5
|
|
39
|
+
source_notes:
|
|
40
|
+
- [[notes/source-a]]
|
|
41
|
+
---
|
|
42
|
+
# Linking Patterns
|
|
43
|
+
|
|
44
|
+
> Connection between steward output and daydream insight closure
|
|
45
|
+
|
|
46
|
+
Steward cycles can close the loop by ingesting high-scoring daydream insights.
|
|
47
|
+
|
|
48
|
+
## Implication
|
|
49
|
+
|
|
50
|
+
Accepted insights become candidate hypotheses for later validation.
|
|
51
|
+
""",
|
|
52
|
+
encoding="utf-8",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_no_env_no_op(service: MemoryService, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
57
|
+
monkeypatch.delenv("MEMORYMASTER_DAYDREAM_INGEST_DIR", raising=False)
|
|
58
|
+
|
|
59
|
+
report = _run_clean_steward(service, tmp_path)
|
|
60
|
+
|
|
61
|
+
assert report["cycles_completed"] == 1
|
|
62
|
+
assert "daydream" not in report
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_env_set_dir_missing_no_crash(
|
|
66
|
+
service: MemoryService,
|
|
67
|
+
tmp_path: Path,
|
|
68
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
69
|
+
) -> None:
|
|
70
|
+
missing = tmp_path / "missing-daydreams"
|
|
71
|
+
monkeypatch.setenv("MEMORYMASTER_DAYDREAM_INGEST_DIR", str(missing))
|
|
72
|
+
|
|
73
|
+
report = _run_clean_steward(service, tmp_path)
|
|
74
|
+
|
|
75
|
+
assert report["cycles_completed"] == 1
|
|
76
|
+
assert report["daydream"] == {"skipped": "dir-not-found", "path": str(missing)}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_env_set_dir_with_insights_ingests(
|
|
80
|
+
service: MemoryService,
|
|
81
|
+
tmp_path: Path,
|
|
82
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
83
|
+
) -> None:
|
|
84
|
+
insights_dir = tmp_path / "Daydreams"
|
|
85
|
+
_write_insight(insights_dir)
|
|
86
|
+
monkeypatch.setenv("MEMORYMASTER_DAYDREAM_INGEST_DIR", str(insights_dir))
|
|
87
|
+
|
|
88
|
+
report = _run_clean_steward(service, tmp_path)
|
|
89
|
+
|
|
90
|
+
assert report["daydream"]["ingested"] == 1
|
|
91
|
+
claims = service.list_claims(status="candidate", limit=20, allow_sensitive=True)
|
|
92
|
+
assert len(claims) == 1
|
|
93
|
+
assert claims[0].source_agent == "daydream"
|
|
94
|
+
assert claims[0].claim_type == "hypothesis"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_ingest_exception_does_not_break_steward(
|
|
98
|
+
service: MemoryService,
|
|
99
|
+
tmp_path: Path,
|
|
100
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
101
|
+
) -> None:
|
|
102
|
+
insights_dir = tmp_path / "Daydreams"
|
|
103
|
+
insights_dir.mkdir()
|
|
104
|
+
monkeypatch.setenv("MEMORYMASTER_DAYDREAM_INGEST_DIR", str(insights_dir))
|
|
105
|
+
|
|
106
|
+
def raise_ingest(*_args: object, **_kwargs: object) -> dict:
|
|
107
|
+
raise RuntimeError("boom from daydream ingest")
|
|
108
|
+
|
|
109
|
+
monkeypatch.setattr("memorymaster.jobs.daydream_ingest.ingest_insights", raise_ingest)
|
|
110
|
+
|
|
111
|
+
report = _run_clean_steward(service, tmp_path)
|
|
112
|
+
|
|
113
|
+
assert report["cycles_completed"] == 1
|
|
114
|
+
assert report["cycles"][0]["budget"]["claims_scanned"] == 0
|
|
115
|
+
assert json.dumps(report["cycles"])
|
|
116
|
+
assert report["daydream"] == {"error": "boom from daydream ingest"}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|