knowledge-rag 4.4.0__tar.gz → 4.5.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.
Files changed (21) hide show
  1. {knowledge_rag-4.4.0 → knowledge_rag-4.5.0}/PKG-INFO +8 -1
  2. {knowledge_rag-4.4.0 → knowledge_rag-4.5.0}/README.md +7 -0
  3. {knowledge_rag-4.4.0 → knowledge_rag-4.5.0}/mcp_server/__init__.py +1 -1
  4. {knowledge_rag-4.4.0 → knowledge_rag-4.5.0}/mcp_server/server.py +35 -4
  5. {knowledge_rag-4.4.0 → knowledge_rag-4.5.0}/pyproject.toml +1 -1
  6. {knowledge_rag-4.4.0 → knowledge_rag-4.5.0}/.gitignore +0 -0
  7. {knowledge_rag-4.4.0 → knowledge_rag-4.5.0}/LICENSE +0 -0
  8. {knowledge_rag-4.4.0 → knowledge_rag-4.5.0}/config.example.yaml +0 -0
  9. {knowledge_rag-4.4.0 → knowledge_rag-4.5.0}/mcp_server/config.py +0 -0
  10. {knowledge_rag-4.4.0 → knowledge_rag-4.5.0}/mcp_server/guarded.py +0 -0
  11. {knowledge_rag-4.4.0 → knowledge_rag-4.5.0}/mcp_server/ingestion.py +0 -0
  12. {knowledge_rag-4.4.0 → knowledge_rag-4.5.0}/mcp_server/instance_lock.py +0 -0
  13. {knowledge_rag-4.4.0 → knowledge_rag-4.5.0}/mcp_server/metrics.py +0 -0
  14. {knowledge_rag-4.4.0 → knowledge_rag-4.5.0}/mcp_server/preflight.py +0 -0
  15. {knowledge_rag-4.4.0 → knowledge_rag-4.5.0}/mcp_server/ratelimit.py +0 -0
  16. {knowledge_rag-4.4.0 → knowledge_rag-4.5.0}/npm/README.md +0 -0
  17. {knowledge_rag-4.4.0 → knowledge_rag-4.5.0}/presets/cybersecurity.yaml +0 -0
  18. {knowledge_rag-4.4.0 → knowledge_rag-4.5.0}/presets/developer.yaml +0 -0
  19. {knowledge_rag-4.4.0 → knowledge_rag-4.5.0}/presets/general.yaml +0 -0
  20. {knowledge_rag-4.4.0 → knowledge_rag-4.5.0}/presets/research.yaml +0 -0
  21. {knowledge_rag-4.4.0 → knowledge_rag-4.5.0}/requirements.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: knowledge-rag
3
- Version: 4.4.0
3
+ Version: 4.5.0
4
4
  Summary: Local RAG System for Claude Code — Hybrid search + Cross-encoder Reranking + 13 MCP Tools + 20 Format Parsers. Zero external servers.
5
5
  Project-URL: Homepage, https://github.com/lyonzin/knowledge-rag
6
6
  Project-URL: Repository, https://github.com/lyonzin/knowledge-rag
@@ -1449,6 +1449,13 @@ Common issues:
1449
1449
 
1450
1450
  ### Unreleased
1451
1451
 
1452
+ ### v4.5.0 (2026-07-06) — Hybrid Search Ranking & Routing Fix
1453
+
1454
+ - **FIX**: `search_knowledge` now searches the entire index when the user omits `category_filter`. The internal `_route_by_keywords()` heuristic previously acted as a hard where-filter on both the semantic and BM25 branches, so queries whose terms happened to map to a sparsely-populated category could return two documents while thousands of relevant chunks in other categories were silently dropped. The router is now **informational only**: the `routed_by` field is still populated in every result for telemetry (public API unchanged), but the candidate set is never restricted. Users who want a hard filter still get it by passing `category_filter=...` explicitly — which continues to filter BM25 consistently with #109. (#112)
1455
+ - **NEW**: Hybrid search adds a small bounded ranking boost when query terms match indexed `source` or `filename` metadata, helping navigational queries surface the canonical file for a topic instead of adjacent files that only cross-reference it. The boost is capped at ~20% of typical RRF magnitudes, so it breaks near-ties without overriding semantic or BM25 signal. Public API unchanged. (#110, thanks @Hohlas)
1456
+ - **TEST**: New `TestKeywordRoutingBehavior` (3 tests) pins the routing fix — BM25 and semantic branches must both see the full corpus when no explicit filter is passed, and explicit `category_filter` still overrides the router. New `TestPathAwareRanking` covers the path-metadata boost. Baseline: 266 → 271.
1457
+ - **UPGRADE NOTE**: warm `query_cache` entries from before v4.5.0 should be invalidated by restarting the server so cached responses no longer reflect the pre-fix restrictive behavior.
1458
+
1452
1459
  ### v4.4.0 (2026-07-06) — Cross-Platform Installer & Hybrid Search Category Filter
1453
1460
 
1454
1461
  - **NEW**: Cross-platform, multi-LLM-client installer (`install.py`) driving both `install.sh` (Linux/macOS) and `install.ps1` (Windows) as thin wrappers. One codebase, one behavior across every OS.
@@ -1401,6 +1401,13 @@ Common issues:
1401
1401
 
1402
1402
  ### Unreleased
1403
1403
 
1404
+ ### v4.5.0 (2026-07-06) — Hybrid Search Ranking & Routing Fix
1405
+
1406
+ - **FIX**: `search_knowledge` now searches the entire index when the user omits `category_filter`. The internal `_route_by_keywords()` heuristic previously acted as a hard where-filter on both the semantic and BM25 branches, so queries whose terms happened to map to a sparsely-populated category could return two documents while thousands of relevant chunks in other categories were silently dropped. The router is now **informational only**: the `routed_by` field is still populated in every result for telemetry (public API unchanged), but the candidate set is never restricted. Users who want a hard filter still get it by passing `category_filter=...` explicitly — which continues to filter BM25 consistently with #109. (#112)
1407
+ - **NEW**: Hybrid search adds a small bounded ranking boost when query terms match indexed `source` or `filename` metadata, helping navigational queries surface the canonical file for a topic instead of adjacent files that only cross-reference it. The boost is capped at ~20% of typical RRF magnitudes, so it breaks near-ties without overriding semantic or BM25 signal. Public API unchanged. (#110, thanks @Hohlas)
1408
+ - **TEST**: New `TestKeywordRoutingBehavior` (3 tests) pins the routing fix — BM25 and semantic branches must both see the full corpus when no explicit filter is passed, and explicit `category_filter` still overrides the router. New `TestPathAwareRanking` covers the path-metadata boost. Baseline: 266 → 271.
1409
+ - **UPGRADE NOTE**: warm `query_cache` entries from before v4.5.0 should be invalidated by restarting the server so cached responses no longer reflect the pre-fix restrictive behavior.
1410
+
1404
1411
  ### v4.4.0 (2026-07-06) — Cross-Platform Installer & Hybrid Search Category Filter
1405
1412
 
1406
1413
  - **NEW**: Cross-platform, multi-LLM-client installer (`install.py`) driving both `install.sh` (Linux/macOS) and `install.ps1` (Windows) as thin wrappers. One codebase, one behavior across every OS.
@@ -8,7 +8,7 @@ import sys # noqa: I001
8
8
  _original_stdout = sys.stdout
9
9
  sys.stdout = sys.stderr
10
10
 
11
- __version__ = "4.4.0"
11
+ __version__ = "4.5.0"
12
12
  __author__ = "Ailton Rocha (Lyon.)"
13
13
 
14
14
  from .config import Config # noqa: E402
@@ -637,6 +637,33 @@ class CrossEncoderReranker:
637
637
  return documents[:top_k]
638
638
 
639
639
 
640
+ def _metadata_path_score(query: str, metadata: Dict[str, Any]) -> float:
641
+ """Return a small generic score boost when query terms match path metadata."""
642
+ query_terms = re.findall(r"[a-z0-9][-a-z0-9]*[a-z0-9]|[a-z0-9]", query.lower())
643
+ if not query_terms:
644
+ return 0.0
645
+
646
+ source = str(metadata.get("source", ""))
647
+ filename = str(metadata.get("filename", ""))
648
+ path_text = f"{source} {filename}".lower()
649
+ path_tokens = set(re.findall(r"[a-z0-9][-a-z0-9]*[a-z0-9]|[a-z0-9]", path_text))
650
+ if not path_tokens:
651
+ return 0.0
652
+
653
+ score = 0.0
654
+ for term in query_terms:
655
+ if term in path_tokens:
656
+ score += 0.0006
657
+ elif term in path_text:
658
+ score += 0.0003
659
+
660
+ query_phrase = query.strip().lower()
661
+ if query_phrase and query_phrase in path_text:
662
+ score += 0.0012
663
+
664
+ return min(score, 0.003)
665
+
666
+
640
667
  # =============================================================================
641
668
  # BM25 INDEX
642
669
  # =============================================================================
@@ -1466,13 +1493,17 @@ class KnowledgeOrchestrator:
1466
1493
 
1467
1494
  self._ensure_bm25_index()
1468
1495
 
1469
- # Keyword routing
1496
+ # Keyword routing — informational only.
1497
+ # `routed_category` is surfaced via the `routed_by` field for telemetry,
1498
+ # but MUST NOT restrict the search when the user did not pass an explicit
1499
+ # `category_filter`. Auto-routing to a sparsely-populated category (e.g. one
1500
+ # with only a handful of docs) was hiding relevant material that lived under
1501
+ # the top-level `security` bucket. Users who want a hard filter still get it
1502
+ # by passing `category_filter=...` explicitly.
1470
1503
  routed_category = self._route_by_keywords(query_text)
1471
1504
  where_filter = None
1472
1505
  if category_filter:
1473
1506
  where_filter = {"category": category_filter}
1474
- elif routed_category:
1475
- where_filter = {"category": routed_category}
1476
1507
 
1477
1508
  def _matches_category(metadata: Dict[str, Any]) -> bool:
1478
1509
  if not where_filter:
@@ -1583,7 +1614,7 @@ class KnowledgeOrchestrator:
1583
1614
  continue
1584
1615
 
1585
1616
  combined_scores[chunk_id] = {
1586
- "rrf_score": combined_rrf,
1617
+ "rrf_score": combined_rrf + _metadata_path_score(query_text, data.get("metadata", {})),
1587
1618
  "semantic_rank": semantic_rank if chunk_id in semantic_results else None,
1588
1619
  "bm25_rank": bm25_rank if chunk_id in bm25_results else None,
1589
1620
  "document": data.get("document", ""),
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "knowledge-rag"
7
- version = "4.4.0"
7
+ version = "4.5.0"
8
8
  description = "Local RAG System for Claude Code — Hybrid search + Cross-encoder Reranking + 13 MCP Tools + 20 Format Parsers. Zero external servers."
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
File without changes
File without changes