local-knowledge 0.2.3__tar.gz → 0.2.4__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.
- {local_knowledge-0.2.3 → local_knowledge-0.2.4}/PKG-INFO +2 -2
- {local_knowledge-0.2.3 → local_knowledge-0.2.4}/pyproject.toml +2 -2
- {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge/server_local.py +37 -1
- {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge.egg-info/PKG-INFO +2 -2
- {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge.egg-info/requires.txt +1 -1
- {local_knowledge-0.2.3 → local_knowledge-0.2.4}/tests/test_agent_doc_consistency.py +21 -0
- {local_knowledge-0.2.3 → local_knowledge-0.2.4}/README.md +0 -0
- {local_knowledge-0.2.3 → local_knowledge-0.2.4}/setup.cfg +0 -0
- {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge/__init__.py +0 -0
- {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge/cli_local.py +0 -0
- {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge/crawl_local.py +0 -0
- {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge/settings_local.py +0 -0
- {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge.egg-info/SOURCES.txt +0 -0
- {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge.egg-info/dependency_links.txt +0 -0
- {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge.egg-info/entry_points.txt +0 -0
- {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge.egg-info/top_level.txt +0 -0
- {local_knowledge-0.2.3 → local_knowledge-0.2.4}/tests/test_crawl_local.py +0 -0
- {local_knowledge-0.2.3 → local_knowledge-0.2.4}/tests/test_extract_files.py +0 -0
- {local_knowledge-0.2.3 → local_knowledge-0.2.4}/tests/test_server_flow_local.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: local-knowledge
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: Local file/folder indexer + grounded-Q&A MCP service — same engine as confluence-knowledge, no credentials needed
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Repository, https://github.com/nkunal1992/agents
|
|
@@ -20,7 +20,7 @@ Requires-Dist: openpyxl>=3.1
|
|
|
20
20
|
Requires-Dist: mcp<2,>=1.6
|
|
21
21
|
Requires-Dist: pip-system-certs>=5.0
|
|
22
22
|
Provides-Extra: models
|
|
23
|
-
Requires-Dist: confluence-knowledge[models]>=0.5.
|
|
23
|
+
Requires-Dist: confluence-knowledge[models]>=0.5.4; extra == "models"
|
|
24
24
|
Provides-Extra: dev
|
|
25
25
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
26
26
|
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "local-knowledge"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.4"
|
|
8
8
|
description = "Local file/folder indexer + grounded-Q&A MCP service — same engine as confluence-knowledge, no credentials needed"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -27,7 +27,7 @@ dependencies = [
|
|
|
27
27
|
|
|
28
28
|
[project.optional-dependencies]
|
|
29
29
|
models = [
|
|
30
|
-
"confluence-knowledge[models]>=0.5.
|
|
30
|
+
"confluence-knowledge[models]>=0.5.4",
|
|
31
31
|
]
|
|
32
32
|
dev = [
|
|
33
33
|
"pytest>=8.0",
|
|
@@ -12,6 +12,7 @@ import socket
|
|
|
12
12
|
import threading
|
|
13
13
|
|
|
14
14
|
from confluence_knowledge.client import AccessError, SecurityException
|
|
15
|
+
from confluence_knowledge.feedback import FeedbackStore
|
|
15
16
|
from confluence_knowledge.index import Index
|
|
16
17
|
from confluence_knowledge.retrieval import (
|
|
17
18
|
MODEL_MARKER as EMBED_MODEL_ID,
|
|
@@ -34,6 +35,7 @@ mcp = FastMCP("local-knowledge")
|
|
|
34
35
|
_scopes: dict[str, dict] = {}
|
|
35
36
|
_evidence_sets: dict[str, dict] = {} # set_id → {scope, ev}
|
|
36
37
|
_indexes: dict[str, Index] = {}
|
|
38
|
+
_feedback: dict[str, FeedbackStore] = {}
|
|
37
39
|
_EMBEDDER = None
|
|
38
40
|
_RERANKER = None
|
|
39
41
|
_SPARSE = None
|
|
@@ -51,6 +53,13 @@ def _index_for(scope_key: str) -> Index:
|
|
|
51
53
|
idx = _indexes[scope_key] = Index(index_root() / f"{scope_key}.db")
|
|
52
54
|
return idx
|
|
53
55
|
|
|
56
|
+
def _feedback_for(scope_key: str) -> FeedbackStore:
|
|
57
|
+
st = _feedback.get(scope_key)
|
|
58
|
+
if st is None:
|
|
59
|
+
st = _feedback[scope_key] = FeedbackStore(
|
|
60
|
+
index_root(), scope_key, "local-knowledge")
|
|
61
|
+
return st
|
|
62
|
+
|
|
54
63
|
|
|
55
64
|
def _embedder():
|
|
56
65
|
global _EMBEDDER
|
|
@@ -267,7 +276,9 @@ def search(scope_handle: str, question: str) -> dict:
|
|
|
267
276
|
return {"evidence": [], "coverage": "stale_model",
|
|
268
277
|
"note": "this index was built with an older embedding model "
|
|
269
278
|
"— run start_crawl once to rebuild it, then search again"}
|
|
270
|
-
|
|
279
|
+
emb = _embedder()
|
|
280
|
+
ev = retrieve(idx, question, emb, _reranker(), sparse=_sparse())
|
|
281
|
+
corrections = _feedback_for(s["key"]).match(emb.embed([question])[0])
|
|
271
282
|
set_id = f"ev:{len(_evidence_sets)}:{s['key'][:8]}"
|
|
272
283
|
# bound the session cache — sets are never reused after the turn ends
|
|
273
284
|
while len(_evidence_sets) >= 32:
|
|
@@ -277,6 +288,7 @@ def search(scope_handle: str, question: str) -> dict:
|
|
|
277
288
|
"evidence_set": set_id,
|
|
278
289
|
"coverage": _coverage(idx),
|
|
279
290
|
"as_of": st["as_of"],
|
|
291
|
+
"user_corrections": corrections,
|
|
280
292
|
"evidence": [
|
|
281
293
|
{"chunk_id": e["chunk_id"], "page_title": e["page_title"],
|
|
282
294
|
"page_url": e["page_url"], "heading": e["heading"],
|
|
@@ -305,6 +317,30 @@ def get_evidence(scope_handle: str, chunk_ids: list[str]) -> dict:
|
|
|
305
317
|
return {"chunks": out}
|
|
306
318
|
|
|
307
319
|
|
|
320
|
+
@mcp.tool()
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
@mcp.tool()
|
|
324
|
+
def submit_feedback(scope_handle: str, question: str, correction: str,
|
|
325
|
+
note: str = "") -> dict:
|
|
326
|
+
"""Store a colleague correction — disclosed alongside the docs on
|
|
327
|
+
similar future questions (never overrides them)."""
|
|
328
|
+
s = _scope(scope_handle)
|
|
329
|
+
if not _models_loaded(0):
|
|
330
|
+
return {"stored": False, "note": LOADING_NOTE}
|
|
331
|
+
qvec = _embedder().embed([question])[0]
|
|
332
|
+
entry = _feedback_for(s["key"]).add(question, qvec, correction, note)
|
|
333
|
+
return {"stored": entry["id"],
|
|
334
|
+
"note": "will be disclosed on similar questions"}
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
@mcp.tool()
|
|
338
|
+
def feedback_report(scope_handle: str) -> dict:
|
|
339
|
+
"""Aggregated corrections — which topics keep getting corrected points
|
|
340
|
+
at outdated/confusing documentation."""
|
|
341
|
+
s = _scope(scope_handle)
|
|
342
|
+
return _feedback_for(s["key"]).report()
|
|
343
|
+
|
|
308
344
|
@mcp.tool()
|
|
309
345
|
def verify_citations(scope_handle: str, evidence_set: str,
|
|
310
346
|
mappings: list[dict]) -> dict:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: local-knowledge
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: Local file/folder indexer + grounded-Q&A MCP service — same engine as confluence-knowledge, no credentials needed
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Repository, https://github.com/nkunal1992/agents
|
|
@@ -20,7 +20,7 @@ Requires-Dist: openpyxl>=3.1
|
|
|
20
20
|
Requires-Dist: mcp<2,>=1.6
|
|
21
21
|
Requires-Dist: pip-system-certs>=5.0
|
|
22
22
|
Provides-Extra: models
|
|
23
|
-
Requires-Dist: confluence-knowledge[models]>=0.5.
|
|
23
|
+
Requires-Dist: confluence-knowledge[models]>=0.5.4; extra == "models"
|
|
24
24
|
Provides-Extra: dev
|
|
25
25
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
26
26
|
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
@@ -29,3 +29,24 @@ def test_agent_references_repo_files_that_exist():
|
|
|
29
29
|
for m in re.findall(r"prompts/([\w.-]+)", doc):
|
|
30
30
|
assert (REPO / "prompts" / m).exists(), \
|
|
31
31
|
f"agent references missing prompts/{m}"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_agent_frontmatter_tools_exist():
|
|
35
|
+
"""Every tool named in the agent's frontmatter must be a real
|
|
36
|
+
@mcp.tool() in the server source."""
|
|
37
|
+
if not AGENT.exists():
|
|
38
|
+
import pytest
|
|
39
|
+
pytest.skip("agent file not in this checkout layout")
|
|
40
|
+
doc = AGENT.read_text()
|
|
41
|
+
tools_line = re.search(r'tools:\s*\[([^\]]+)\]', doc).group(1)
|
|
42
|
+
app = AGENT.stem.replace(".agent", "")
|
|
43
|
+
declared = {t.strip().strip('"').split("/")[1]
|
|
44
|
+
for t in tools_line.split(",") if f"{app}/" in t}
|
|
45
|
+
srv_name = {"local-knowledge": "server_local",
|
|
46
|
+
"sharepoint-knowledge": "server_sp",
|
|
47
|
+
"confluence-knowledge": "server"}[app]
|
|
48
|
+
pkg = app.replace("-", "_")
|
|
49
|
+
srv = (Path(__file__).parents[1] / "src" / pkg / f"{srv_name}.py").read_text()
|
|
50
|
+
real = set(re.findall(r"@mcp\.tool\(\)\s*\ndef\s+(\w+)", srv))
|
|
51
|
+
missing = declared - real
|
|
52
|
+
assert not missing, f"agent frontmatter names tools not in server: {missing}"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|