knowledge-master 0.4.0__tar.gz → 0.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.
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/PKG-INFO +1 -1
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master.egg-info/PKG-INFO +1 -1
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master.egg-info/SOURCES.txt +6 -1
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/pyproject.toml +1 -1
- knowledge_master-0.5.0/tests/test_connectors.py +58 -0
- knowledge_master-0.5.0/tests/test_dedup.py +24 -0
- knowledge_master-0.5.0/tests/test_migrations.py +23 -0
- knowledge_master-0.5.0/tests/test_reranker.py +43 -0
- knowledge_master-0.5.0/tests/test_ts_parsers.py +96 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/LICENSE +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/README.md +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master/__init__.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master/__main__.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master/api.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master/chunking.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master/cli.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master/connectors.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master/embeddings.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master/intelligence.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master/migrations.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master/parsers/__init__.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master/parsers/git_repo.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master/parsers/markdown.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master/rerank.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master/server.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master/static_analysis.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master/store.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master/ts_parsers.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master/watcher.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master/web.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master.egg-info/dependency_links.txt +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master.egg-info/entry_points.txt +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master.egg-info/requires.txt +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master.egg-info/top_level.txt +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/setup.cfg +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/tests/test_api.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/tests/test_chunking.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/tests/test_cli.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/tests/test_intelligence.py +0 -0
- {knowledge_master-0.4.0 → knowledge_master-0.5.0}/tests/test_static_analysis.py +0 -0
|
@@ -29,5 +29,10 @@ knowledge_master/parsers/markdown.py
|
|
|
29
29
|
tests/test_api.py
|
|
30
30
|
tests/test_chunking.py
|
|
31
31
|
tests/test_cli.py
|
|
32
|
+
tests/test_connectors.py
|
|
33
|
+
tests/test_dedup.py
|
|
32
34
|
tests/test_intelligence.py
|
|
33
|
-
tests/
|
|
35
|
+
tests/test_migrations.py
|
|
36
|
+
tests/test_reranker.py
|
|
37
|
+
tests/test_static_analysis.py
|
|
38
|
+
tests/test_ts_parsers.py
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Tests for connector framework."""
|
|
2
|
+
from knowledge_master.connectors import MCPSource, SOURCES, add_custom_source, _parse_mcp_result
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_preconfigured_sources_exist():
|
|
6
|
+
assert "outlook" in SOURCES
|
|
7
|
+
assert "slack" in SOURCES
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_source_has_required_fields():
|
|
11
|
+
for name, source in SOURCES.items():
|
|
12
|
+
assert source.name, f"{name} missing name"
|
|
13
|
+
assert source.command, f"{name} missing command"
|
|
14
|
+
assert source.tool_name, f"{name} missing tool_name"
|
|
15
|
+
assert source.source_type, f"{name} missing source_type"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_add_custom_source():
|
|
19
|
+
add_custom_source("test-source", ["echo", "hi"], "get_data", {"limit": 10}, "custom")
|
|
20
|
+
assert "test-source" in SOURCES
|
|
21
|
+
assert SOURCES["test-source"].tool_name == "get_data"
|
|
22
|
+
# Cleanup
|
|
23
|
+
del SOURCES["test-source"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_parse_mcp_result_json_list():
|
|
27
|
+
class FakeContent:
|
|
28
|
+
text = '[{"title": "hello", "body": "world"}]'
|
|
29
|
+
|
|
30
|
+
class FakeResult:
|
|
31
|
+
content = [FakeContent()]
|
|
32
|
+
|
|
33
|
+
items = _parse_mcp_result(FakeResult())
|
|
34
|
+
assert len(items) == 1
|
|
35
|
+
assert items[0]["title"] == "hello"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_parse_mcp_result_json_dict_with_results():
|
|
39
|
+
class FakeContent:
|
|
40
|
+
text = '{"results": [{"text": "a"}, {"text": "b"}]}'
|
|
41
|
+
|
|
42
|
+
class FakeResult:
|
|
43
|
+
content = [FakeContent()]
|
|
44
|
+
|
|
45
|
+
items = _parse_mcp_result(FakeResult())
|
|
46
|
+
assert len(items) == 2
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_parse_mcp_result_plain_text():
|
|
50
|
+
class FakeContent:
|
|
51
|
+
text = "just plain text"
|
|
52
|
+
|
|
53
|
+
class FakeResult:
|
|
54
|
+
content = [FakeContent()]
|
|
55
|
+
|
|
56
|
+
items = _parse_mcp_result(FakeResult())
|
|
57
|
+
assert len(items) == 1
|
|
58
|
+
assert items[0]["text"] == "just plain text"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Tests for store deduplication and content hashing."""
|
|
2
|
+
from knowledge_master.store import content_hash
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_content_hash_deterministic():
|
|
6
|
+
h1 = content_hash("hello world")
|
|
7
|
+
h2 = content_hash("hello world")
|
|
8
|
+
assert h1 == h2
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_content_hash_different_for_different_content():
|
|
12
|
+
h1 = content_hash("hello world")
|
|
13
|
+
h2 = content_hash("hello world!")
|
|
14
|
+
assert h1 != h2
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_content_hash_length():
|
|
18
|
+
h = content_hash("test")
|
|
19
|
+
assert len(h) == 16 # sha256 truncated to 16 chars
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_content_hash_hex():
|
|
23
|
+
h = content_hash("test")
|
|
24
|
+
assert all(c in "0123456789abcdef" for c in h)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Tests for schema migrations."""
|
|
2
|
+
from knowledge_master.migrations import (
|
|
3
|
+
CURRENT_SCHEMA_VERSION,
|
|
4
|
+
MIGRATIONS,
|
|
5
|
+
_migrate_to_v1,
|
|
6
|
+
_migrate_to_v3,
|
|
7
|
+
_migrate_to_v4,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_current_schema_version():
|
|
12
|
+
assert CURRENT_SCHEMA_VERSION == 4
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_all_migrations_defined():
|
|
16
|
+
for v in range(1, CURRENT_SCHEMA_VERSION + 1):
|
|
17
|
+
assert v in MIGRATIONS, f"Migration to v{v} not defined"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_migrations_are_callable():
|
|
21
|
+
for v, fn in MIGRATIONS.items():
|
|
22
|
+
assert callable(fn)
|
|
23
|
+
assert fn.__doc__ is not None, f"Migration v{v} has no docstring"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Benchmark test for re-ranker — compare raw cosine vs re-ranked ordering.
|
|
2
|
+
|
|
3
|
+
This is an integration test (requires Ollama) but demonstrates the value of re-ranking.
|
|
4
|
+
Run with: pytest tests/integration/test_reranker_benchmark.py -v
|
|
5
|
+
"""
|
|
6
|
+
import pytest
|
|
7
|
+
from knowledge_master.rerank import rerank, _cosine_sim
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_cosine_sim_identical():
|
|
11
|
+
a = [1.0, 0.0, 0.0]
|
|
12
|
+
assert _cosine_sim(a, a) == pytest.approx(1.0)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_cosine_sim_orthogonal():
|
|
16
|
+
a = [1.0, 0.0, 0.0]
|
|
17
|
+
b = [0.0, 1.0, 0.0]
|
|
18
|
+
assert _cosine_sim(a, b) == pytest.approx(0.0)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_cosine_sim_opposite():
|
|
22
|
+
a = [1.0, 0.0]
|
|
23
|
+
b = [-1.0, 0.0]
|
|
24
|
+
assert _cosine_sim(a, b) == pytest.approx(-1.0)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_rerank_empty():
|
|
28
|
+
result = rerank("test query", [], top_k=5)
|
|
29
|
+
assert result == []
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_rerank_preserves_fields():
|
|
33
|
+
"""Re-rank should keep all original result fields."""
|
|
34
|
+
results = [
|
|
35
|
+
{"text": "hello world", "source": "a.py", "score": 0.5, "extra": "keep"},
|
|
36
|
+
]
|
|
37
|
+
# This would normally call Ollama — skip if not available
|
|
38
|
+
try:
|
|
39
|
+
ranked = rerank("hello", results, top_k=1)
|
|
40
|
+
assert "extra" in ranked[0]
|
|
41
|
+
assert "rerank_score" in ranked[0]
|
|
42
|
+
except Exception:
|
|
43
|
+
pytest.skip("Ollama not available")
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Tests for tree-sitter based language parsers."""
|
|
2
|
+
import tempfile
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from knowledge_master.ts_parsers import (
|
|
6
|
+
extract_typescript_graph,
|
|
7
|
+
extract_go_graph,
|
|
8
|
+
extract_rust_graph,
|
|
9
|
+
resolve_ts_import,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_typescript_import_extraction():
|
|
14
|
+
with tempfile.NamedTemporaryFile(suffix=".ts", mode="w", delete=False) as f:
|
|
15
|
+
f.write('import { useState } from "react";\n')
|
|
16
|
+
f.write('import { helper } from "./utils";\n')
|
|
17
|
+
f.write("export function App() { return null; }\n")
|
|
18
|
+
f.flush()
|
|
19
|
+
result = extract_typescript_graph(f.name)
|
|
20
|
+
|
|
21
|
+
assert len(result["imports"]) == 2
|
|
22
|
+
assert result["imports"][0]["module"] == "react"
|
|
23
|
+
assert result["imports"][1]["module"] == "./utils"
|
|
24
|
+
assert len(result["exports"]) >= 1
|
|
25
|
+
assert any(e["name"] == "App" for e in result["exports"])
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_typescript_require_extraction():
|
|
29
|
+
with tempfile.NamedTemporaryFile(suffix=".js", mode="w", delete=False) as f:
|
|
30
|
+
f.write('const fs = require("fs");\n')
|
|
31
|
+
f.flush()
|
|
32
|
+
result = extract_typescript_graph(f.name)
|
|
33
|
+
|
|
34
|
+
assert len(result["imports"]) >= 1
|
|
35
|
+
assert result["imports"][0]["module"] == "fs"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_go_import_extraction():
|
|
39
|
+
with tempfile.NamedTemporaryFile(suffix=".go", mode="w", delete=False) as f:
|
|
40
|
+
f.write('package main\n\nimport (\n\t"fmt"\n\t"os"\n)\n\nfunc Main() {}\n')
|
|
41
|
+
f.flush()
|
|
42
|
+
result = extract_go_graph(f.name)
|
|
43
|
+
|
|
44
|
+
assert len(result["imports"]) >= 2
|
|
45
|
+
modules = [i["module"] for i in result["imports"]]
|
|
46
|
+
assert "fmt" in modules
|
|
47
|
+
assert "os" in modules
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_go_exported_functions():
|
|
51
|
+
with tempfile.NamedTemporaryFile(suffix=".go", mode="w", delete=False) as f:
|
|
52
|
+
f.write('package pkg\n\nfunc PublicFunc() {}\nfunc privateFunc() {}\n')
|
|
53
|
+
f.flush()
|
|
54
|
+
result = extract_go_graph(f.name)
|
|
55
|
+
|
|
56
|
+
names = [e["name"] for e in result["exports"]]
|
|
57
|
+
assert "PublicFunc" in names
|
|
58
|
+
assert "privateFunc" not in names
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_rust_use_extraction():
|
|
62
|
+
with tempfile.NamedTemporaryFile(suffix=".rs", mode="w", delete=False) as f:
|
|
63
|
+
f.write("use std::collections::HashMap;\nuse crate::utils;\n\npub fn hello() {}\nfn private() {}\n")
|
|
64
|
+
f.flush()
|
|
65
|
+
result = extract_rust_graph(f.name)
|
|
66
|
+
|
|
67
|
+
assert len(result["imports"]) >= 2
|
|
68
|
+
modules = [i["module"] for i in result["imports"]]
|
|
69
|
+
assert "std" in modules
|
|
70
|
+
assert any(e["name"] == "hello" for e in result["exports"])
|
|
71
|
+
assert not any(e["name"] == "private" for e in result["exports"])
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_rust_mod_detection():
|
|
75
|
+
with tempfile.NamedTemporaryFile(suffix=".rs", mode="w", delete=False) as f:
|
|
76
|
+
f.write("mod parser;\nmod utils;\n")
|
|
77
|
+
f.flush()
|
|
78
|
+
result = extract_rust_graph(f.name)
|
|
79
|
+
|
|
80
|
+
mod_imports = [i for i in result["imports"] if i.get("is_mod")]
|
|
81
|
+
assert len(mod_imports) == 2
|
|
82
|
+
assert mod_imports[0]["module"] == "parser"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_ts_import_resolution():
|
|
86
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
87
|
+
(Path(tmp) / "src").mkdir()
|
|
88
|
+
(Path(tmp) / "src" / "utils.ts").write_text("export const x = 1;")
|
|
89
|
+
(Path(tmp) / "src" / "index.ts").write_text("")
|
|
90
|
+
|
|
91
|
+
result = resolve_ts_import("./utils", "src/index.ts", tmp)
|
|
92
|
+
assert result == "src/utils.ts"
|
|
93
|
+
|
|
94
|
+
# External package — should return None
|
|
95
|
+
result = resolve_ts_import("react", "src/index.ts", tmp)
|
|
96
|
+
assert result is None
|
|
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
|
{knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{knowledge_master-0.4.0 → knowledge_master-0.5.0}/knowledge_master.egg-info/entry_points.txt
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
|