memorymaster 3.5.1__tar.gz → 3.5.2__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.5.1/memorymaster.egg-info → memorymaster-3.5.2}/PKG-INFO +1 -1
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/config_templates/hooks/memorymaster-steward-cycle.py +15 -4
- {memorymaster-3.5.1 → memorymaster-3.5.2/memorymaster.egg-info}/PKG-INFO +1 -1
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster.egg-info/SOURCES.txt +3 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/pyproject.toml +1 -1
- memorymaster-3.5.2/scripts/grid_recall_weights.py +191 -0
- memorymaster-3.5.2/tests/test_hook_env_isolation.py +79 -0
- memorymaster-3.5.2/tests/test_llm_provider_claude_cli.py +186 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/LICENSE +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/README.md +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/artifacts/bm25-per-field-eval-harness.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/artifacts/l2-haiku-batches/extract.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/benchmarks/longmemeval_runner.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/benchmarks/longmemeval_vector_runner.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/benchmarks/perf_smoke.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/examples/run_conversation_opd.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/examples/run_conversation_replay.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/examples/run_conversation_rl.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/__init__.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/__main__.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/api_server.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/bedrock_client.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/calendar_client.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/claw_adapter.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/cli.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/config.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/config_store.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/data_formatter.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/idle_detector.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/launcher.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/log_color.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/openclaw_env_rollout.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/prm_scorer.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/rollout.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/runtime_state.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/scheduler.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/sdk_backend.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/setup_wizard.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/skill_evolver.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/skill_manager.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/trainer.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/metaclaw/utils.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/scripts/run_v03_benchmark.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/tests/conftest.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/tests/test_cli.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/tests/test_launcher.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/tests/test_openclaw_env_rollout.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/tests/test_runtime_state.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/tests/test_sdk_backend.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/tests/test_setup_wizard.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/tests/test_utils.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/tests/test_v03_live_tinker.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/agent-skill-creator/references/examples/stock-analyzer/scripts/main.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/agent-skill-creator/scripts/export_utils.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/agent-skill-creator/scripts/security_scan.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/agent-skill-creator/scripts/skill_registry.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/agent-skill-creator/scripts/staleness_check.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/agent-skill-creator/scripts/validate.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/__init__.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/__main__.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/_storage_lifecycle.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/_storage_read.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/_storage_schema.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/_storage_shared.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/_storage_write_claims.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/access_control.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/auto_extractor.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/auto_resolver.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/claim_verifier.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/cli.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/cli_handlers_basic.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/cli_handlers_curation.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/cli_helpers.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/config.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/config_templates/claude-md-append.md +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/config_templates/codex-agents-md-append.md +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/config_templates/hooks/memorymaster-auto-ingest.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/config_templates/hooks/memorymaster-classify.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/config_templates/hooks/memorymaster-dream-sync.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/config_templates/hooks/memorymaster-observe.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/config_templates/hooks/memorymaster-precompact.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/config_templates/hooks/memorymaster-recall.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/config_templates/hooks/memorymaster-session-start.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/config_templates/hooks/memorymaster-validate-wiki.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/conflict_resolver.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/context_hook.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/context_optimizer.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/daily_notes.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/dashboard.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/db_merge.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/dream_bridge.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/embeddings.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/entity_extractor.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/entity_graph.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/entity_registry.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/feedback.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/graph_store.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/hook_log.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/jobs/__init__.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/jobs/compact_summaries.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/jobs/compactor.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/jobs/decay.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/jobs/dedup.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/jobs/deterministic.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/jobs/extractor.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/jobs/staleness.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/jobs/validator.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/key_rotator.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/lifecycle.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/llm_provider.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/llm_steward.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/mcp_server.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/metrics_exporter.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/models.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/operator.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/operator_queue.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/plugins.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/policy.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/postgres_store.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/qdrant_backend.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/qdrant_recall_fallback.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/qmd_bridge.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/query_classifier.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/query_expansion.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/recall_fusion.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/recall_tokenizer.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/retrieval.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/retry.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/review.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/rl_trainer.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/scheduler.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/schema.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/schema.sql +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/schema_postgres.sql +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/security.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/service.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/session_tracker.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/setup_hooks.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/skill_evolver.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/snapshot.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/steward.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/steward_classifier.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/steward_features.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/storage.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/store_factory.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/transcript_miner.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/turn_schema.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/vault_bases.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/vault_curator.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/vault_exporter.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/vault_linter.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/vault_log.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/vault_query_capture.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/vault_synthesis.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/verbatim_recall.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/verbatim_store.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/webhook.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/wiki_engine.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/wiki_freshness.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster/wiki_similarity.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster.egg-info/dependency_links.txt +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster.egg-info/entry_points.txt +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster.egg-info/requires.txt +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/memorymaster.egg-info/top_level.txt +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/agg_recall_latency.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/alert_operator_metrics.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/autoresearch_daemon.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/backfill_entity_extraction.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/backfill_graph_store.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/backfill_stop_hook_citations.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/backtest_steward_classifier.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/build_steward_training_set.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/check_hook_template_drift.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/claude_to_turns.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/codex_live_to_turns.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/compaction_edge_cases.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/compaction_trace_report.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/compaction_trace_validate.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/confusion_matrix_eval.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/conversation_importer.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/conversation_to_turns.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/e2e_operator.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/email_live_to_turns.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/eval_bm25_sweep.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/eval_classify_f1.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/eval_memorymaster.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/eval_recall_precision_at_5.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/eval_recall_quality.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/eval_steward_pareto.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/eval_verbatim_recall.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/expand_recall_eval.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/generate_drill_signoff.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/git_to_turns.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/github_live_to_turns.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/gitnexus_to_claims.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/index_claims_to_qdrant.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/ingest_planning_docs.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/jira_live_to_turns.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/llm_benchmark.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/merge_scope_variants.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/messages_to_turns.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/operator_metrics.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/recurring_incident_drill.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/release_readiness.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/run_codex_autologger.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/run_incident_drill.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/run_longmemeval.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/scheduled_ingest.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/setup-hooks.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/slack_live_to_turns.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/sync_hook_templates.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/tickets_to_turns.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/train_steward_classifier.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/scripts/webhook_to_turns.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/setup.cfg +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/conftest.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/integration/test_extract_llm_ollama_live.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_access_control.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_auto_extractor.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_auto_ingest_hook_citations.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_auto_ingest_hook_schema.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_auto_resolver.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_auto_validate.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_bm25_per_field.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_claim_links.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_classify_hook_f1.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_classify_hook_latency.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_claude_to_turns.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_cli_json_flag.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_cli_ready.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_cli_review_queue.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_cli_subcommands.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_compact_summaries.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_compaction_trace.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_config.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_conflict_resolver.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_confusion_matrix_eval.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_connection_retry.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_connectors.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_context_hook.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_context_optimizer.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_conversation_to_turns.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_dashboard.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_dedup.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_deterministic_predicates.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_embeddings_coverage.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_entity_extractor.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_entity_extractor_llm.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_entity_graph.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_entity_new_kinds.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_entity_registry.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_eval_harness.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_events_schema.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_extract_llm_ollama.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_feedback.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_fts5_search.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_graph_distance.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_graph_store.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_handler_regressions.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_human_id.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_incident_drill_runner.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_integration_workflows.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_key_rotator.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_lifecycle.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_llm_fallback.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_llm_steward_coverage.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_llm_steward_key_rotation.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_mcp_helpers.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_metrics_exporter.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_obsidian_mind_patterns.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_operator.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_operator_queue.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_perf_smoke_config.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_plugins.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_policy_coverage.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_policy_mode_env.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_postgres_parity.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_qdrant_backend.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_qmd_bridge.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_query_classifier.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_query_expansion.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_recall_entity_fanout.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_recall_fusion.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_recall_latency.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_recall_precision_at_5.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_recall_tokenizer.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_recall_vector_fallback.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_reliability_hardening.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_review.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_rl_trainer.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_rrf_auto_gate.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_scheduler.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_schema.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_scope_boost.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_security_access.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_security_patterns.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_sensitivity_filter_adversarial.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_sensitivity_filter_adversarial_v2.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_service_coverage.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_session_tracker.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_snapshot.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_sqlite_core.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_staleness.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_stealth_mode.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_steward.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_steward_classifier.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_steward_features.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_steward_features_v3.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_steward_resolution_parity.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_store_factory.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_tenant_isolation.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_turn_schema.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_vault_exporter.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_vector_search.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_verbatim_recall.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_webhook.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_wiki_binding.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_wiki_freshness.py +0 -0
- {memorymaster-3.5.1 → memorymaster-3.5.2}/tests/test_wiki_similarity_multiscope.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memorymaster
|
|
3
|
-
Version: 3.5.
|
|
3
|
+
Version: 3.5.2
|
|
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
|
|
@@ -6,6 +6,19 @@ DB_PATH = os.path.join(PROJECT_ROOT, "memorymaster.db")
|
|
|
6
6
|
|
|
7
7
|
sys.path.insert(0, PROJECT_ROOT)
|
|
8
8
|
os.environ["MEMORYMASTER_DEFAULT_DB"] = DB_PATH
|
|
9
|
+
|
|
10
|
+
# LLM stack: claude_cli (Claude Code OAuth via local `claude --print`) is the
|
|
11
|
+
# primary, with Ollama gemma4:e4b as a defensive fallback. Direct assignment
|
|
12
|
+
# (NOT setdefault) — the hook MUST own these vars so an inherited shell env
|
|
13
|
+
# can't silently route LLM calls to a stale provider. Bug observed 2026-04-25:
|
|
14
|
+
# setdefault was a no-op when the inherited env already had MEMORYMASTER_LLM_PROVIDER
|
|
15
|
+
# set, so the new model name routed to the OLD provider → 50× HTTP 404 per cycle
|
|
16
|
+
# before the fallback chain saved it. Captured as v3.5.0 release notes.
|
|
17
|
+
os.environ["MEMORYMASTER_LLM_PROVIDER"] = "claude_cli"
|
|
18
|
+
os.environ["MEMORYMASTER_LLM_MODEL"] = "claude-haiku-4-5-20251001"
|
|
19
|
+
os.environ["MEMORYMASTER_LLM_FALLBACK_PROVIDER"] = "ollama"
|
|
20
|
+
os.environ["MEMORYMASTER_LLM_FALLBACK_MODEL"] = "gemma4:e4b"
|
|
21
|
+
|
|
9
22
|
os.chdir(PROJECT_ROOT)
|
|
10
23
|
|
|
11
24
|
try:
|
|
@@ -36,11 +49,9 @@ try:
|
|
|
36
49
|
except Exception as e:
|
|
37
50
|
print(f"[MemoryMaster] auto-archive error: {e}", file=sys.stderr)
|
|
38
51
|
|
|
39
|
-
# Wiki absorb (compiled truth + timeline articles)
|
|
52
|
+
# Wiki absorb (compiled truth + timeline articles). Inherits the LLM provider
|
|
53
|
+
# block above — uses the same OAuth-backed haiku stack as the steward.
|
|
40
54
|
try:
|
|
41
|
-
# Keys come from the rotator file (~/.memorymaster/gemini-keys.env) or a
|
|
42
|
-
# singular GEMINI_API_KEY env var. Hook must never hardcode credentials.
|
|
43
|
-
os.environ.setdefault("MEMORYMASTER_LLM_PROVIDER", "google")
|
|
44
55
|
from memorymaster.wiki_engine import absorb
|
|
45
56
|
wiki_path = os.path.join(PROJECT_ROOT, "obsidian-vault", "wiki")
|
|
46
57
|
stats = absorb(DB_PATH, wiki_path)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memorymaster
|
|
3
|
-
Version: 3.5.
|
|
3
|
+
Version: 3.5.2
|
|
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
|
|
@@ -189,6 +189,7 @@ scripts/generate_drill_signoff.py
|
|
|
189
189
|
scripts/git_to_turns.py
|
|
190
190
|
scripts/github_live_to_turns.py
|
|
191
191
|
scripts/gitnexus_to_claims.py
|
|
192
|
+
scripts/grid_recall_weights.py
|
|
192
193
|
scripts/index_claims_to_qdrant.py
|
|
193
194
|
scripts/ingest_planning_docs.py
|
|
194
195
|
scripts/jira_live_to_turns.py
|
|
@@ -251,12 +252,14 @@ tests/test_fts5_search.py
|
|
|
251
252
|
tests/test_graph_distance.py
|
|
252
253
|
tests/test_graph_store.py
|
|
253
254
|
tests/test_handler_regressions.py
|
|
255
|
+
tests/test_hook_env_isolation.py
|
|
254
256
|
tests/test_human_id.py
|
|
255
257
|
tests/test_incident_drill_runner.py
|
|
256
258
|
tests/test_integration_workflows.py
|
|
257
259
|
tests/test_key_rotator.py
|
|
258
260
|
tests/test_lifecycle.py
|
|
259
261
|
tests/test_llm_fallback.py
|
|
262
|
+
tests/test_llm_provider_claude_cli.py
|
|
260
263
|
tests/test_llm_steward_coverage.py
|
|
261
264
|
tests/test_llm_steward_key_rotation.py
|
|
262
265
|
tests/test_mcp_helpers.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "memorymaster"
|
|
7
|
-
version = "3.5.
|
|
7
|
+
version = "3.5.2"
|
|
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"}]
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""Grid-search the recall weight knobs against precision@5.
|
|
2
|
+
|
|
3
|
+
Sweeps W_LEXICAL × W_FRESHNESS × W_GRAPH against the existing 100-prompt
|
|
4
|
+
evaluation harness (`scripts/eval_recall_precision_at_5.py`) and writes a
|
|
5
|
+
sorted markdown table + raw JSONL log so a future tweak is reproducible.
|
|
6
|
+
|
|
7
|
+
W_VECTOR is skipped because the local DB has no Qdrant; the stream is a
|
|
8
|
+
no-op without `MEMORYMASTER_USE_QDRANT=1` and a populated index.
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
python scripts/grid_recall_weights.py \
|
|
12
|
+
--prompts artifacts/real-prompts-100.jsonl \
|
|
13
|
+
--db memorymaster.db \
|
|
14
|
+
--output artifacts/recall-weight-tuning-2026-04-26.md
|
|
15
|
+
"""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import argparse
|
|
19
|
+
import itertools
|
|
20
|
+
import json
|
|
21
|
+
import os
|
|
22
|
+
import re
|
|
23
|
+
import subprocess
|
|
24
|
+
import sys
|
|
25
|
+
import time
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
|
|
28
|
+
# Modest 3 × 3 × 4 = 36 grid. Bounded by ~10s/combo wall via subprocess startup.
|
|
29
|
+
W_LEXICAL_GRID = (0.2, 0.3, 0.4)
|
|
30
|
+
W_FRESHNESS_GRID = (0.0, 0.05, 0.1)
|
|
31
|
+
W_GRAPH_GRID = (0.0, 0.05, 0.1, 0.2)
|
|
32
|
+
|
|
33
|
+
METRIC_RE = {
|
|
34
|
+
"precision@5": re.compile(r"precision@5\s*=\s*([\d.]+)"),
|
|
35
|
+
"MAP@5": re.compile(r"MAP@5\s*=\s*([\d.]+)"),
|
|
36
|
+
"hit@5": re.compile(r"hit@5\s*=\s*([\d.]+)"),
|
|
37
|
+
"p95_ms": re.compile(r"p95\s*=\s*([\d.]+)\s*ms"),
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _run_eval(
|
|
42
|
+
eval_script: Path,
|
|
43
|
+
prompts: Path,
|
|
44
|
+
db: Path,
|
|
45
|
+
weights: dict,
|
|
46
|
+
json_out: Path,
|
|
47
|
+
label: str,
|
|
48
|
+
) -> dict | None:
|
|
49
|
+
env = os.environ.copy()
|
|
50
|
+
for k, v in weights.items():
|
|
51
|
+
env[f"MEMORYMASTER_RECALL_{k}"] = str(v)
|
|
52
|
+
# The GRAPH stream is opt-in: W_GRAPH alone is a no-op unless the stream
|
|
53
|
+
# itself is enabled. Turn it on only when the weight is non-zero — keeps
|
|
54
|
+
# the latency-cost cells out of the grid when they can't possibly help.
|
|
55
|
+
if weights.get("W_GRAPH", 0) > 0:
|
|
56
|
+
env["MEMORYMASTER_RECALL_GRAPH"] = "1"
|
|
57
|
+
# Same for the freshness stream.
|
|
58
|
+
if weights.get("W_FRESHNESS", 0) > 0:
|
|
59
|
+
env["MEMORYMASTER_RECALL_FRESHNESS"] = "1"
|
|
60
|
+
|
|
61
|
+
proc = subprocess.run(
|
|
62
|
+
[
|
|
63
|
+
sys.executable,
|
|
64
|
+
str(eval_script),
|
|
65
|
+
"--prompts",
|
|
66
|
+
str(prompts),
|
|
67
|
+
"--db",
|
|
68
|
+
str(db),
|
|
69
|
+
"--json-out",
|
|
70
|
+
str(json_out),
|
|
71
|
+
"--label",
|
|
72
|
+
label,
|
|
73
|
+
],
|
|
74
|
+
capture_output=True,
|
|
75
|
+
text=True,
|
|
76
|
+
env=env,
|
|
77
|
+
timeout=600,
|
|
78
|
+
)
|
|
79
|
+
if proc.returncode != 0:
|
|
80
|
+
return {"error": proc.stderr.strip()[:200] or "non-zero exit"}
|
|
81
|
+
|
|
82
|
+
out = proc.stdout
|
|
83
|
+
parsed = {"label": label, **{k: v for k, v in weights.items()}}
|
|
84
|
+
for metric, rgx in METRIC_RE.items():
|
|
85
|
+
m = rgx.search(out)
|
|
86
|
+
parsed[metric] = float(m.group(1)) if m else None
|
|
87
|
+
return parsed
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def main() -> int:
|
|
91
|
+
p = argparse.ArgumentParser(description=__doc__)
|
|
92
|
+
p.add_argument("--prompts", type=Path, required=True)
|
|
93
|
+
p.add_argument("--db", type=Path, required=True)
|
|
94
|
+
p.add_argument("--output", type=Path, required=True)
|
|
95
|
+
p.add_argument(
|
|
96
|
+
"--eval-script",
|
|
97
|
+
type=Path,
|
|
98
|
+
default=Path("scripts/eval_recall_precision_at_5.py"),
|
|
99
|
+
)
|
|
100
|
+
p.add_argument(
|
|
101
|
+
"--per-run-json-dir",
|
|
102
|
+
type=Path,
|
|
103
|
+
default=Path("artifacts/grid-runs"),
|
|
104
|
+
help="Per-cell raw eval JSONL dump directory.",
|
|
105
|
+
)
|
|
106
|
+
args = p.parse_args()
|
|
107
|
+
|
|
108
|
+
args.per_run_json_dir.mkdir(parents=True, exist_ok=True)
|
|
109
|
+
|
|
110
|
+
combos = list(
|
|
111
|
+
itertools.product(W_LEXICAL_GRID, W_FRESHNESS_GRID, W_GRAPH_GRID)
|
|
112
|
+
)
|
|
113
|
+
print(f"[grid] running {len(combos)} cells over W_LEXICAL × W_FRESHNESS × W_GRAPH")
|
|
114
|
+
|
|
115
|
+
rows: list[dict] = []
|
|
116
|
+
t_total = time.monotonic()
|
|
117
|
+
for i, (w_lex, w_fresh, w_graph) in enumerate(combos, 1):
|
|
118
|
+
weights = {"W_LEXICAL": w_lex, "W_FRESHNESS": w_fresh, "W_GRAPH": w_graph}
|
|
119
|
+
label = f"L{w_lex}_F{w_fresh}_G{w_graph}"
|
|
120
|
+
json_out = args.per_run_json_dir / f"{label}.jsonl"
|
|
121
|
+
t0 = time.monotonic()
|
|
122
|
+
row = _run_eval(args.eval_script, args.prompts, args.db, weights, json_out, label)
|
|
123
|
+
wall = time.monotonic() - t0
|
|
124
|
+
if row is None:
|
|
125
|
+
row = {"error": "no output"}
|
|
126
|
+
row["wall_s"] = round(wall, 1)
|
|
127
|
+
rows.append(row)
|
|
128
|
+
prec = row.get("precision@5")
|
|
129
|
+
prec_str = f"{prec:.3f}" if isinstance(prec, float) else "ERR"
|
|
130
|
+
print(f"[grid] {i}/{len(combos)} {label} wall={wall:.1f}s p@5={prec_str}")
|
|
131
|
+
|
|
132
|
+
# Pick best by precision@5 (tie-break MAP@5 desc, then p95 asc)
|
|
133
|
+
valid = [r for r in rows if isinstance(r.get("precision@5"), float)]
|
|
134
|
+
valid.sort(
|
|
135
|
+
key=lambda r: (
|
|
136
|
+
-r["precision@5"],
|
|
137
|
+
-(r.get("MAP@5") or 0.0),
|
|
138
|
+
r.get("p95_ms") or 1e9,
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# Write markdown report
|
|
143
|
+
lines = [
|
|
144
|
+
"# Recall weight grid — precision@5 tuning",
|
|
145
|
+
"",
|
|
146
|
+
f"- Eval prompts: `{args.prompts}` (100, 70 labeled)",
|
|
147
|
+
f"- DB: `{args.db}` (post-L2-backfill snapshot)",
|
|
148
|
+
f"- Grid: W_LEXICAL × W_FRESHNESS × W_GRAPH = "
|
|
149
|
+
f"{len(W_LEXICAL_GRID)} × {len(W_FRESHNESS_GRID)} × {len(W_GRAPH_GRID)} = {len(combos)} cells",
|
|
150
|
+
f"- Total wall: {round(time.monotonic()-t_total, 1)}s",
|
|
151
|
+
"",
|
|
152
|
+
"## Top 10 by precision@5",
|
|
153
|
+
"",
|
|
154
|
+
"| W_LEXICAL | W_FRESHNESS | W_GRAPH | precision@5 | MAP@5 | hit@5 | p95 ms | wall s |",
|
|
155
|
+
"|---|---|---|---|---|---|---|---|",
|
|
156
|
+
]
|
|
157
|
+
for r in valid[:10]:
|
|
158
|
+
lines.append(
|
|
159
|
+
f"| {r['W_LEXICAL']} | {r['W_FRESHNESS']} | {r['W_GRAPH']} "
|
|
160
|
+
f"| {r['precision@5']:.3f} | {r.get('MAP@5'):.3f} | {r.get('hit@5'):.3f} "
|
|
161
|
+
f"| {r.get('p95_ms')} | {r['wall_s']} |"
|
|
162
|
+
)
|
|
163
|
+
if not valid:
|
|
164
|
+
lines.append("| — | — | — | NO VALID RUNS | | | | |")
|
|
165
|
+
else:
|
|
166
|
+
winner = valid[0]
|
|
167
|
+
lines += [
|
|
168
|
+
"",
|
|
169
|
+
"## Winner",
|
|
170
|
+
"",
|
|
171
|
+
f"`MEMORYMASTER_RECALL_W_LEXICAL={winner['W_LEXICAL']}` "
|
|
172
|
+
f"`MEMORYMASTER_RECALL_W_FRESHNESS={winner['W_FRESHNESS']}` "
|
|
173
|
+
f"`MEMORYMASTER_RECALL_W_GRAPH={winner['W_GRAPH']}`",
|
|
174
|
+
"",
|
|
175
|
+
f"precision@5 = **{winner['precision@5']:.3f}** "
|
|
176
|
+
f"(baseline 0.152, delta = {(winner['precision@5'] - 0.152):+.3f})",
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
args.output.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
180
|
+
|
|
181
|
+
# Also dump rows as JSON for downstream automation
|
|
182
|
+
json_path = args.output.with_suffix(".json")
|
|
183
|
+
json_path.write_text(json.dumps(rows, indent=2), encoding="utf-8")
|
|
184
|
+
|
|
185
|
+
print(f"[grid] wrote {args.output}")
|
|
186
|
+
print(f"[grid] wrote {json_path}")
|
|
187
|
+
return 0
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
if __name__ == "__main__":
|
|
191
|
+
sys.exit(main())
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Regression tests for the steward-cycle hook env wiring (v3.5.0 fix).
|
|
2
|
+
|
|
3
|
+
Bug: the deployed hook used `os.environ.setdefault("MEMORYMASTER_LLM_PROVIDER", ...)`,
|
|
4
|
+
which is a no-op when the inherited shell env already has the var set. After
|
|
5
|
+
switching the primary to claude_cli, an inherited `MEMORYMASTER_LLM_PROVIDER=google`
|
|
6
|
+
left the provider unchanged — but `MEMORYMASTER_LLM_MODEL` was set to the new
|
|
7
|
+
Claude model name, producing 50× HTTP 404 from the Gemini API per cycle before
|
|
8
|
+
the fallback chain saved it.
|
|
9
|
+
|
|
10
|
+
Fix: hook MUST use direct `os.environ["KEY"] = ...` assignment.
|
|
11
|
+
|
|
12
|
+
These tests assert against the SHIPPED template
|
|
13
|
+
(`memorymaster/config_templates/hooks/memorymaster-steward-cycle.py`) so a
|
|
14
|
+
reverted edit fails CI before the broken hook reaches a user via
|
|
15
|
+
`memorymaster-setup`.
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
import pytest
|
|
22
|
+
|
|
23
|
+
TEMPLATE = (
|
|
24
|
+
Path(__file__).resolve().parents[1]
|
|
25
|
+
/ "memorymaster"
|
|
26
|
+
/ "config_templates"
|
|
27
|
+
/ "hooks"
|
|
28
|
+
/ "memorymaster-steward-cycle.py"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@pytest.fixture(scope="module")
|
|
33
|
+
def template_text() -> str:
|
|
34
|
+
assert TEMPLATE.exists(), f"steward hook template missing: {TEMPLATE}"
|
|
35
|
+
return TEMPLATE.read_text(encoding="utf-8")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_template_does_not_use_setdefault_for_llm_provider(template_text: str) -> None:
|
|
39
|
+
"""If this fails, someone reverted the v3.5.0 hook env-assignment fix."""
|
|
40
|
+
assert (
|
|
41
|
+
'setdefault("MEMORYMASTER_LLM_PROVIDER"' not in template_text
|
|
42
|
+
), "steward hook must use os.environ['KEY'] = ... assignment, not setdefault"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_template_does_not_use_setdefault_for_llm_model(template_text: str) -> None:
|
|
46
|
+
assert (
|
|
47
|
+
'setdefault("MEMORYMASTER_LLM_MODEL"' not in template_text
|
|
48
|
+
), "steward hook must use os.environ['KEY'] = ... assignment, not setdefault"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@pytest.mark.parametrize(
|
|
52
|
+
"key,expected_value",
|
|
53
|
+
[
|
|
54
|
+
("MEMORYMASTER_LLM_PROVIDER", "claude_cli"),
|
|
55
|
+
("MEMORYMASTER_LLM_MODEL", "claude-haiku-4-5-20251001"),
|
|
56
|
+
("MEMORYMASTER_LLM_FALLBACK_PROVIDER", "ollama"),
|
|
57
|
+
("MEMORYMASTER_LLM_FALLBACK_MODEL", "gemma4:e4b"),
|
|
58
|
+
],
|
|
59
|
+
)
|
|
60
|
+
def test_template_assigns_env_directly(
|
|
61
|
+
template_text: str, key: str, expected_value: str
|
|
62
|
+
) -> None:
|
|
63
|
+
"""Each LLM env var must be set via direct assignment with the v3.5.0 default."""
|
|
64
|
+
needle = f'os.environ["{key}"] = "{expected_value}"'
|
|
65
|
+
assert (
|
|
66
|
+
needle in template_text
|
|
67
|
+
), f"expected `{needle}` in template; either the assignment or value drifted"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_template_imports_only_from_stdlib_and_memorymaster(template_text: str) -> None:
|
|
71
|
+
"""The hook is invoked from cron/Task Scheduler; it can only depend on
|
|
72
|
+
stdlib + the installed memorymaster package. A new third-party import here
|
|
73
|
+
would silently break user installs that didn't pip install the dep."""
|
|
74
|
+
forbidden = ("requests", "httpx", "aiohttp", "boto3")
|
|
75
|
+
for pkg in forbidden:
|
|
76
|
+
assert (
|
|
77
|
+
f"import {pkg}" not in template_text
|
|
78
|
+
and f"from {pkg}" not in template_text
|
|
79
|
+
), f"steward hook must not import {pkg} (would break installs missing the dep)"
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""Tests for the claude_cli LLM provider (memorymaster.llm_provider._call_claude_cli).
|
|
2
|
+
|
|
3
|
+
The provider shells out to the local `claude --print` binary. These tests mock
|
|
4
|
+
subprocess.run so they don't actually invoke the CLI — they cover the defensive
|
|
5
|
+
branches that the production user is most likely to hit:
|
|
6
|
+
|
|
7
|
+
- missing binary on PATH
|
|
8
|
+
- non-zero exit
|
|
9
|
+
- timeout
|
|
10
|
+
- UTF-8 round-trip including emoji
|
|
11
|
+
- MEMORYMASTER_CLAUDE_CLI_BIN override
|
|
12
|
+
- MEMORYMASTER_CLAUDE_CLI_TIMEOUT override
|
|
13
|
+
- aliases ("claude_cli", "claude-cli") resolve to the same provider
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import subprocess
|
|
18
|
+
from unittest.mock import patch
|
|
19
|
+
|
|
20
|
+
import pytest
|
|
21
|
+
|
|
22
|
+
from memorymaster import llm_provider
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _completed(stdout: str = "", stderr: str = "", returncode: int = 0):
|
|
26
|
+
return subprocess.CompletedProcess(
|
|
27
|
+
args=["claude", "--print", "--model", "x"],
|
|
28
|
+
returncode=returncode,
|
|
29
|
+
stdout=stdout,
|
|
30
|
+
stderr=stderr,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@pytest.fixture(autouse=True)
|
|
35
|
+
def _clean_env(monkeypatch):
|
|
36
|
+
"""Strip any inherited claude_cli env vars so each test starts clean."""
|
|
37
|
+
for key in (
|
|
38
|
+
"MEMORYMASTER_CLAUDE_CLI_BIN",
|
|
39
|
+
"MEMORYMASTER_CLAUDE_CLI_TIMEOUT",
|
|
40
|
+
"MEMORYMASTER_LLM_MODEL",
|
|
41
|
+
):
|
|
42
|
+
monkeypatch.delenv(key, raising=False)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_missing_binary_returns_empty(monkeypatch, caplog):
|
|
46
|
+
"""When `claude` is not on PATH and no override is set, return '' and warn."""
|
|
47
|
+
monkeypatch.setattr(llm_provider.shutil, "which", lambda _: None)
|
|
48
|
+
with caplog.at_level("WARNING"):
|
|
49
|
+
out = llm_provider._call_claude_cli("prompt", "text")
|
|
50
|
+
assert out == ""
|
|
51
|
+
assert any("binary not found" in rec.message for rec in caplog.records)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_non_zero_exit_returns_empty(monkeypatch, caplog):
|
|
55
|
+
monkeypatch.setattr(llm_provider.shutil, "which", lambda _: "/fake/claude")
|
|
56
|
+
fake_run = lambda *a, **kw: _completed(stdout="", stderr="boom", returncode=2)
|
|
57
|
+
monkeypatch.setattr(llm_provider.subprocess, "run", fake_run)
|
|
58
|
+
with caplog.at_level("WARNING"):
|
|
59
|
+
out = llm_provider._call_claude_cli("prompt", "text")
|
|
60
|
+
assert out == ""
|
|
61
|
+
assert any("exit=2" in rec.message for rec in caplog.records)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_timeout_returns_empty(monkeypatch, caplog):
|
|
65
|
+
monkeypatch.setattr(llm_provider.shutil, "which", lambda _: "/fake/claude")
|
|
66
|
+
|
|
67
|
+
def _raise(*_a, **_kw):
|
|
68
|
+
raise subprocess.TimeoutExpired(cmd="claude", timeout=120)
|
|
69
|
+
|
|
70
|
+
monkeypatch.setattr(llm_provider.subprocess, "run", _raise)
|
|
71
|
+
with caplog.at_level("WARNING"):
|
|
72
|
+
out = llm_provider._call_claude_cli("prompt", "text")
|
|
73
|
+
assert out == ""
|
|
74
|
+
assert any("timed out" in rec.message for rec in caplog.records)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_oserror_returns_empty(monkeypatch, caplog):
|
|
78
|
+
monkeypatch.setattr(llm_provider.shutil, "which", lambda _: "/fake/claude")
|
|
79
|
+
|
|
80
|
+
def _raise(*_a, **_kw):
|
|
81
|
+
raise OSError("permission denied")
|
|
82
|
+
|
|
83
|
+
monkeypatch.setattr(llm_provider.subprocess, "run", _raise)
|
|
84
|
+
with caplog.at_level("WARNING"):
|
|
85
|
+
out = llm_provider._call_claude_cli("prompt", "text")
|
|
86
|
+
assert out == ""
|
|
87
|
+
assert any("subprocess failed" in rec.message for rec in caplog.records)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_utf8_emoji_roundtrip(monkeypatch):
|
|
91
|
+
monkeypatch.setattr(llm_provider.shutil, "which", lambda _: "/fake/claude")
|
|
92
|
+
captured = {}
|
|
93
|
+
|
|
94
|
+
def _fake(args, input, capture_output, text, timeout, encoding, errors):
|
|
95
|
+
captured["input"] = input
|
|
96
|
+
captured["encoding"] = encoding
|
|
97
|
+
return _completed(stdout="hola 👋 ñoño \n", returncode=0)
|
|
98
|
+
|
|
99
|
+
monkeypatch.setattr(llm_provider.subprocess, "run", _fake)
|
|
100
|
+
out = llm_provider._call_claude_cli("Saludá", "ño 🌍 emoji")
|
|
101
|
+
assert out == "hola 👋 ñoño" # strip() applied
|
|
102
|
+
assert captured["input"] == "Saludá\n\nño 🌍 emoji"
|
|
103
|
+
assert captured["encoding"] == "utf-8"
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_bin_override_honored(monkeypatch):
|
|
107
|
+
monkeypatch.setenv("MEMORYMASTER_CLAUDE_CLI_BIN", "/custom/claude")
|
|
108
|
+
# `which` should NOT be consulted when override is set.
|
|
109
|
+
monkeypatch.setattr(
|
|
110
|
+
llm_provider.shutil,
|
|
111
|
+
"which",
|
|
112
|
+
lambda _: pytest.fail("which should not be called when override set"),
|
|
113
|
+
)
|
|
114
|
+
captured = {}
|
|
115
|
+
|
|
116
|
+
def _fake(args, **_kw):
|
|
117
|
+
captured["argv"] = args
|
|
118
|
+
return _completed(stdout="ok", returncode=0)
|
|
119
|
+
|
|
120
|
+
monkeypatch.setattr(llm_provider.subprocess, "run", _fake)
|
|
121
|
+
out = llm_provider._call_claude_cli("p", "t")
|
|
122
|
+
assert out == "ok"
|
|
123
|
+
assert captured["argv"][0] == "/custom/claude"
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def test_timeout_override_honored(monkeypatch):
|
|
127
|
+
monkeypatch.setenv("MEMORYMASTER_CLAUDE_CLI_TIMEOUT", "30")
|
|
128
|
+
monkeypatch.setattr(llm_provider.shutil, "which", lambda _: "/fake/claude")
|
|
129
|
+
captured = {}
|
|
130
|
+
|
|
131
|
+
def _fake(args, **kw):
|
|
132
|
+
captured["timeout"] = kw["timeout"]
|
|
133
|
+
return _completed(stdout="ok", returncode=0)
|
|
134
|
+
|
|
135
|
+
monkeypatch.setattr(llm_provider.subprocess, "run", _fake)
|
|
136
|
+
llm_provider._call_claude_cli("p", "t")
|
|
137
|
+
assert captured["timeout"] == 30
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def test_model_override_honored(monkeypatch):
|
|
141
|
+
monkeypatch.setenv("MEMORYMASTER_LLM_MODEL", "claude-sonnet-4-6-20251022")
|
|
142
|
+
monkeypatch.setattr(llm_provider.shutil, "which", lambda _: "/fake/claude")
|
|
143
|
+
captured = {}
|
|
144
|
+
|
|
145
|
+
def _fake(args, **_kw):
|
|
146
|
+
captured["argv"] = args
|
|
147
|
+
return _completed(stdout="ok", returncode=0)
|
|
148
|
+
|
|
149
|
+
monkeypatch.setattr(llm_provider.subprocess, "run", _fake)
|
|
150
|
+
llm_provider._call_claude_cli("p", "t")
|
|
151
|
+
assert "--model" in captured["argv"]
|
|
152
|
+
model_idx = captured["argv"].index("--model") + 1
|
|
153
|
+
assert captured["argv"][model_idx] == "claude-sonnet-4-6-20251022"
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def test_default_model_is_haiku(monkeypatch):
|
|
157
|
+
monkeypatch.setattr(llm_provider.shutil, "which", lambda _: "/fake/claude")
|
|
158
|
+
captured = {}
|
|
159
|
+
|
|
160
|
+
def _fake(args, **_kw):
|
|
161
|
+
captured["argv"] = args
|
|
162
|
+
return _completed(stdout="ok", returncode=0)
|
|
163
|
+
|
|
164
|
+
monkeypatch.setattr(llm_provider.subprocess, "run", _fake)
|
|
165
|
+
llm_provider._call_claude_cli("p", "t")
|
|
166
|
+
model_idx = captured["argv"].index("--model") + 1
|
|
167
|
+
assert captured["argv"][model_idx] == "claude-haiku-4-5-20251001"
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def test_provider_aliases_register_same_function():
|
|
171
|
+
"""Both 'claude_cli' and 'claude-cli' must resolve to _call_claude_cli."""
|
|
172
|
+
assert llm_provider._PROVIDERS["claude_cli"] is llm_provider._call_claude_cli
|
|
173
|
+
assert llm_provider._PROVIDERS["claude-cli"] is llm_provider._call_claude_cli
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def test_call_llm_dispatches_to_claude_cli(monkeypatch):
|
|
177
|
+
"""End-to-end: call_llm with provider=claude_cli should route to _call_claude_cli."""
|
|
178
|
+
monkeypatch.setenv("MEMORYMASTER_LLM_PROVIDER", "claude_cli")
|
|
179
|
+
monkeypatch.setattr(llm_provider.shutil, "which", lambda _: "/fake/claude")
|
|
180
|
+
|
|
181
|
+
def _fake(args, **_kw):
|
|
182
|
+
return _completed(stdout='[{"answer": 42}]', returncode=0)
|
|
183
|
+
|
|
184
|
+
monkeypatch.setattr(llm_provider.subprocess, "run", _fake)
|
|
185
|
+
out = llm_provider.call_llm("Q:", "What is the answer?")
|
|
186
|
+
assert out == '[{"answer": 42}]'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/examples/run_conversation_replay.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/MetaClaw/tests/test_openclaw_env_rollout.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{memorymaster-3.5.1 → memorymaster-3.5.2}/cloned/agent-skill-creator/scripts/export_utils.py
RENAMED
|
File without changes
|