knowledge-rag 3.3.1__tar.gz → 3.3.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: knowledge-rag
3
- Version: 3.3.1
3
+ Version: 3.3.2
4
4
  Summary: Local RAG System for Claude Code — Hybrid search + Cross-encoder Reranking + 12 MCP Tools. 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
@@ -38,7 +38,7 @@ Description-Content-Type: text/markdown
38
38
 
39
39
  <div align="center">
40
40
 
41
- ![Version](https://img.shields.io/badge/version-3.3.1-blue.svg)
41
+ ![Version](https://img.shields.io/badge/version-3.3.2-blue.svg)
42
42
  ![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12-green.svg)
43
43
  ![License](https://img.shields.io/badge/license-MIT-yellow.svg)
44
44
  ![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey.svg)
@@ -1163,7 +1163,7 @@ pip install rank-bm25
1163
1163
 
1164
1164
  ### "ModuleNotFoundError: No module named 'mcp_server'"
1165
1165
 
1166
- This occurs when Claude Code doesn't set the working directory correctly. Use the `cmd /c "cd /d ... && python"` wrapper in your MCP config (see [Installation](#configure-mcp-for-claude-code)).
1166
+ This occurs when Claude Code doesn't set the working directory correctly. Use the `cmd /c "cd /d ... && python"` wrapper in your MCP config (see [Installation](#installation)).
1167
1167
 
1168
1168
  ### Dimension mismatch after upgrade
1169
1169
 
@@ -1195,6 +1195,15 @@ With ~200 documents, expect ~300-500MB RAM. The embedding model (~50MB) and rera
1195
1195
 
1196
1196
  ## Changelog
1197
1197
 
1198
+ ### v3.3.2 (2026-04-06)
1199
+
1200
+ - **FIX**: Full type validation on all YAML config values — wrong types warn and fall back to defaults
1201
+ - **FIX**: Bounds validation for chunk_size, chunk_overlap, default_results, max_results, embedding_dim
1202
+ - **FIX**: `keyword_routes` with string values instead of lists detected and removed with warning
1203
+ - **FIX**: `reranker_enabled: "yes"` (string) corrected to boolean with warning
1204
+ - **FIX**: Synced version strings across all source files
1205
+ - **FIX**: Broken README anchor, duplicate keyword, error handling in `knowledge-rag init`
1206
+
1198
1207
  ### v3.3.1 (2026-04-06)
1199
1208
 
1200
1209
  - **FIX**: YAML null values (`category_mappings:` without value) no longer crash the server — falls back to defaults
@@ -2,7 +2,7 @@
2
2
 
3
3
  <div align="center">
4
4
 
5
- ![Version](https://img.shields.io/badge/version-3.3.1-blue.svg)
5
+ ![Version](https://img.shields.io/badge/version-3.3.2-blue.svg)
6
6
  ![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12-green.svg)
7
7
  ![License](https://img.shields.io/badge/license-MIT-yellow.svg)
8
8
  ![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey.svg)
@@ -1127,7 +1127,7 @@ pip install rank-bm25
1127
1127
 
1128
1128
  ### "ModuleNotFoundError: No module named 'mcp_server'"
1129
1129
 
1130
- This occurs when Claude Code doesn't set the working directory correctly. Use the `cmd /c "cd /d ... && python"` wrapper in your MCP config (see [Installation](#configure-mcp-for-claude-code)).
1130
+ This occurs when Claude Code doesn't set the working directory correctly. Use the `cmd /c "cd /d ... && python"` wrapper in your MCP config (see [Installation](#installation)).
1131
1131
 
1132
1132
  ### Dimension mismatch after upgrade
1133
1133
 
@@ -1159,6 +1159,15 @@ With ~200 documents, expect ~300-500MB RAM. The embedding model (~50MB) and rera
1159
1159
 
1160
1160
  ## Changelog
1161
1161
 
1162
+ ### v3.3.2 (2026-04-06)
1163
+
1164
+ - **FIX**: Full type validation on all YAML config values — wrong types warn and fall back to defaults
1165
+ - **FIX**: Bounds validation for chunk_size, chunk_overlap, default_results, max_results, embedding_dim
1166
+ - **FIX**: `keyword_routes` with string values instead of lists detected and removed with warning
1167
+ - **FIX**: `reranker_enabled: "yes"` (string) corrected to boolean with warning
1168
+ - **FIX**: Synced version strings across all source files
1169
+ - **FIX**: Broken README anchor, duplicate keyword, error handling in `knowledge-rag init`
1170
+
1162
1171
  ### v3.3.1 (2026-04-06)
1163
1172
 
1164
1173
  - **FIX**: YAML null values (`category_mappings:` without value) no longer crash the server — falls back to defaults
@@ -1,6 +1,6 @@
1
1
  """Knowledge RAG MCP Server - Local Retrieval-Augmented Generation System"""
2
2
 
3
- __version__ = "3.2.3"
3
+ __version__ = "3.3.2"
4
4
  __author__ = "Ailton Rocha (Lyon.)"
5
5
 
6
6
  from .config import Config
@@ -1,4 +1,4 @@
1
- """Configuration for Knowledge RAG System v3.1 — YAML-configurable"""
1
+ """Configuration for Knowledge RAG System v3.3.2 — YAML-configurable"""
2
2
 
3
3
  import os
4
4
  from dataclasses import dataclass, field
@@ -73,7 +73,19 @@ def _get(section: str, key: str, default):
73
73
  if not isinstance(s, dict):
74
74
  return default
75
75
  val = s.get(key)
76
- return val if val is not None else default
76
+ if val is None:
77
+ return default
78
+ # Skip type check when default is None (caller handles validation)
79
+ if default is None:
80
+ return val
81
+ # YAML parses "yes"/"no" as bool, but explicit string "yes" stays str
82
+ if not isinstance(val, type(default)):
83
+ print(
84
+ f"[WARN] config.yaml: {section}.{key} has wrong type "
85
+ f"(expected {type(default).__name__}, got {type(val).__name__}), using default"
86
+ )
87
+ return default
88
+ return val
77
89
 
78
90
 
79
91
  def _get_top(key: str, default):
@@ -81,7 +93,8 @@ def _get_top(key: str, default):
81
93
  val = _yaml.get(key)
82
94
  if val is None:
83
95
  return default
84
- if not isinstance(val, type(default)):
96
+ if not isinstance(val, dict):
97
+ print(f"[WARN] config.yaml: {key} has wrong type (expected dict, got {type(val).__name__}), using default")
85
98
  return default
86
99
  return val
87
100
 
@@ -155,7 +168,6 @@ _DEFAULT_KEYWORD_ROUTES = {
155
168
  "deserialization",
156
169
  "ysoserial",
157
170
  "upload bypass",
158
- "reverse shell",
159
171
  "web shell",
160
172
  "hash cracking",
161
173
  "hashcat",
@@ -431,7 +443,41 @@ class Config:
431
443
  max_results: int = field(default_factory=lambda: _get("search", "max_results", 20))
432
444
 
433
445
  def __post_init__(self):
434
- """Ensure directories exist"""
446
+ """Validate config values and ensure directories exist."""
447
+ # Bounds validation
448
+ if not isinstance(self.chunk_size, int) or self.chunk_size < 100:
449
+ print(f"[WARN] chunk_size={self.chunk_size} invalid, using 1000")
450
+ self.chunk_size = 1000
451
+ if not isinstance(self.chunk_overlap, int) or self.chunk_overlap < 0:
452
+ print(f"[WARN] chunk_overlap={self.chunk_overlap} invalid, using 200")
453
+ self.chunk_overlap = 200
454
+ if self.chunk_overlap >= self.chunk_size:
455
+ print(
456
+ f"[WARN] chunk_overlap ({self.chunk_overlap}) >= chunk_size ({self.chunk_size}), using {self.chunk_size // 5}"
457
+ )
458
+ self.chunk_overlap = self.chunk_size // 5
459
+ if not isinstance(self.default_results, int) or self.default_results < 1:
460
+ self.default_results = 5
461
+ if not isinstance(self.max_results, int) or self.max_results < 1:
462
+ self.max_results = 20
463
+ if not isinstance(self.embedding_dim, int) or self.embedding_dim < 1:
464
+ self.embedding_dim = 384
465
+ if not isinstance(self.reranker_enabled, bool):
466
+ print(f"[WARN] reranker_enabled={self.reranker_enabled!r} invalid, using True")
467
+ self.reranker_enabled = True
468
+ if not isinstance(self.reranker_top_k_multiplier, int) or self.reranker_top_k_multiplier < 1:
469
+ self.reranker_top_k_multiplier = 3
470
+ if not isinstance(self.supported_formats, list) or not self.supported_formats:
471
+ print("[WARN] supported_formats is empty or invalid, using defaults")
472
+ self.supported_formats = [".md", ".txt", ".pdf", ".py", ".json", ".docx", ".xlsx", ".pptx", ".csv"]
473
+
474
+ # Validate keyword_routes values are lists (not strings)
475
+ for cat, keywords in list(self.keyword_routes.items()):
476
+ if not isinstance(keywords, list):
477
+ print(f"[WARN] keyword_routes.{cat} is not a list, removing")
478
+ del self.keyword_routes[cat]
479
+
480
+ # Ensure directories exist
435
481
  self.data_dir.mkdir(parents=True, exist_ok=True)
436
482
  self.chroma_dir.mkdir(parents=True, exist_ok=True)
437
483
  self.documents_dir.mkdir(parents=True, exist_ok=True)
@@ -19,8 +19,8 @@ Features:
19
19
  - CRUD operations via MCP tools (add, update, remove docs)
20
20
 
21
21
  Autor: Lyon (Ailton Rocha)
22
- Versao: 3.0.0
23
- Data: 2026-03-19
22
+ Versao: 3.3.2
23
+ Data: 2026-04-06
24
24
  """
25
25
 
26
26
  import hashlib
@@ -1836,32 +1836,37 @@ def _handle_init():
1836
1836
 
1837
1837
  cwd = Path.cwd()
1838
1838
 
1839
- # Copy config.example.yaml
1840
- src = data_dir / "config.example.yaml"
1841
- if src.exists():
1842
- dst = cwd / "config.example.yaml"
1843
- shutil.copy2(src, dst)
1844
- print(f"[OK] {dst}")
1845
-
1846
- # Copy presets
1847
- presets_dir = cwd / "presets"
1848
- presets_dir.mkdir(exist_ok=True)
1849
- for f in data_dir.glob("*.yaml"):
1850
- if f.name == "config.example.yaml":
1851
- continue
1852
- dst = presets_dir / f.name
1853
- shutil.copy2(f, dst)
1854
- print(f"[OK] {dst}")
1855
-
1856
- # Create documents dir
1857
- docs_dir = cwd / "documents"
1858
- docs_dir.mkdir(exist_ok=True)
1859
- print(f"[OK] {docs_dir}/")
1860
-
1861
- print("\nDone. Quick start:")
1862
- print(" cp presets/general.yaml config.yaml # or cybersecurity, developer, research")
1863
- print(" # Add your documents to documents/")
1864
- print(" # Restart Claude Code")
1839
+ try:
1840
+ # Copy config.example.yaml
1841
+ src = data_dir / "config.example.yaml"
1842
+ if src.exists():
1843
+ dst = cwd / "config.example.yaml"
1844
+ shutil.copy2(src, dst)
1845
+ print(f"[OK] {dst}")
1846
+
1847
+ # Copy presets
1848
+ presets_dir = cwd / "presets"
1849
+ presets_dir.mkdir(exist_ok=True)
1850
+ for f in data_dir.glob("*.yaml"):
1851
+ if f.name == "config.example.yaml":
1852
+ continue
1853
+ dst = presets_dir / f.name
1854
+ shutil.copy2(f, dst)
1855
+ print(f"[OK] {dst}")
1856
+
1857
+ # Create documents dir
1858
+ docs_dir = cwd / "documents"
1859
+ docs_dir.mkdir(exist_ok=True)
1860
+ print(f"[OK] {docs_dir}/")
1861
+
1862
+ print("\nDone. Quick start:")
1863
+ print(" cp presets/general.yaml config.yaml # or cybersecurity, developer, research")
1864
+ print(" # Add your documents to documents/")
1865
+ print(" # Restart Claude Code")
1866
+ except PermissionError:
1867
+ print("[ERROR] Permission denied. Run from a writable directory.")
1868
+ except OSError as e:
1869
+ print(f"[ERROR] Failed to write files: {e}")
1865
1870
 
1866
1871
 
1867
1872
  def main():
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "knowledge-rag"
7
- version = "3.3.1"
7
+ version = "3.3.2"
8
8
  description = "Local RAG System for Claude Code — Hybrid search + Cross-encoder Reranking + 12 MCP Tools. Zero external servers."
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
File without changes
File without changes