Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [1.3.0] - 2026-07-27

### Added

- **Multi-field lexical arms (`recall.text_search_fields`)**: title/heading fields are usually far more precise lexical signals than chunk bodies — under `text_search: lexical`, a mapping of payload field → fusion weight (YAML `text_search_fields: {title: 2.0, text: 1.0}`, env `MNEMOSTACK_TEXT_SEARCH_FIELDS="title:2.0,text:1.0"`) turns each field into its OWN lexically-gated arm: the MatchText gate runs on that field while the result text still comes from `text_key` (a hit surfaced only by the title arm carries its chunk body, not its title, into fusion and synthesis). Arms get distinct names (`qdrant_text` for the `text_key` field, `qdrant_text:<field>` otherwise), so fusion weights, the adaptive (Q-learning) profile, and latency/hit/trace/degraded telemetry stay per-arm — previously two same-class arms shared one name and were indistinguishable everywhere that matters; `Retriever` subclasses now accept a `name=` constructor override for exactly that reason. Weights apply at the fusion level (the gate filters, it cannot score); weight 1.0 is left to the adaptive profile, anything else is a static override. When set, the mapping REPLACES the arm set (list `text_key` explicitly to keep the body arm), and it requires `text_search: lexical` — any other mode fails loud at startup instead of silently ignoring the fields. Wired on every surface (HTTP server, MCP, CLI): `mnemostack text-index` creates the full-text index for EVERY configured gate field, `doctor` live-checks each one and validates the fields/mode pairing, and `--source bm25` still selects the whole lexical family (suffixed names match by their base). Verified against real Qdrant servers: per-field text indexes and title-gated matches replay in the CI smoke suite.
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "mnemostack"
version = "1.2.0"
version = "1.3.0"
description = "Self-hosted hybrid memory & retrieval for AI apps: semantic + BM25 + temporal + graph recall over Qdrant/Memgraph, RRF fusion, 8-stage ranking, multi-tenant filters, answer synthesis."
readme = "README.md"
requires-python = ">=3.10"
Expand Down
2 changes: 1 addition & 1 deletion src/mnemostack/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
slow and can leave async test runners waiting on unrelated imports.
"""

__version__ = "1.2.0"
__version__ = "1.3.0"

__all__ = [
"Config",
Expand Down
11 changes: 11 additions & 0 deletions tests/test_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,14 @@ def test_package_version_matches_project_metadata():

assert match is not None
assert mnemostack.__version__ == match.group(1)


def test_current_version_has_dated_changelog_entry():
changelog = Path("CHANGELOG.md").read_text()
version_heading = re.search(
rf"^## \[{re.escape(mnemostack.__version__)}\] - \d{{4}}-\d{{2}}-\d{{2}}$",
changelog,
re.MULTILINE,
)

assert version_heading is not None
Loading