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.
Files changed (19) hide show
  1. {local_knowledge-0.2.3 → local_knowledge-0.2.4}/PKG-INFO +2 -2
  2. {local_knowledge-0.2.3 → local_knowledge-0.2.4}/pyproject.toml +2 -2
  3. {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge/server_local.py +37 -1
  4. {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge.egg-info/PKG-INFO +2 -2
  5. {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge.egg-info/requires.txt +1 -1
  6. {local_knowledge-0.2.3 → local_knowledge-0.2.4}/tests/test_agent_doc_consistency.py +21 -0
  7. {local_knowledge-0.2.3 → local_knowledge-0.2.4}/README.md +0 -0
  8. {local_knowledge-0.2.3 → local_knowledge-0.2.4}/setup.cfg +0 -0
  9. {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge/__init__.py +0 -0
  10. {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge/cli_local.py +0 -0
  11. {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge/crawl_local.py +0 -0
  12. {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge/settings_local.py +0 -0
  13. {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge.egg-info/SOURCES.txt +0 -0
  14. {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge.egg-info/dependency_links.txt +0 -0
  15. {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge.egg-info/entry_points.txt +0 -0
  16. {local_knowledge-0.2.3 → local_knowledge-0.2.4}/src/local_knowledge.egg-info/top_level.txt +0 -0
  17. {local_knowledge-0.2.3 → local_knowledge-0.2.4}/tests/test_crawl_local.py +0 -0
  18. {local_knowledge-0.2.3 → local_knowledge-0.2.4}/tests/test_extract_files.py +0 -0
  19. {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
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.3; extra == "models"
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.3"
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.3",
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
- ev = retrieve(idx, question, _embedder(), _reranker(), sparse=_sparse())
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
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.3; extra == "models"
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"
@@ -12,4 +12,4 @@ pytest-cov>=5.0
12
12
  ruff>=0.6
13
13
 
14
14
  [models]
15
- confluence-knowledge[models]>=0.5.3
15
+ confluence-knowledge[models]>=0.5.4
@@ -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}"