sharepoint-knowledge 0.2.2__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.
- {sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/PKG-INFO +2 -2
- {sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/pyproject.toml +2 -2
- {sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/src/sharepoint_knowledge/crawl_sp.py +11 -1
- {sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/src/sharepoint_knowledge/server_sp.py +49 -8
- {sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/src/sharepoint_knowledge.egg-info/PKG-INFO +2 -2
- {sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/src/sharepoint_knowledge.egg-info/requires.txt +1 -1
- {sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/tests/test_agent_doc_consistency.py +21 -0
- {sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/README.md +0 -0
- {sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/setup.cfg +0 -0
- {sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/src/sharepoint_knowledge/__init__.py +0 -0
- {sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/src/sharepoint_knowledge/auth_sp.py +0 -0
- {sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/src/sharepoint_knowledge/cli_sp.py +0 -0
- {sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/src/sharepoint_knowledge/client_sp.py +0 -0
- {sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/src/sharepoint_knowledge/settings_sp.py +0 -0
- {sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/src/sharepoint_knowledge.egg-info/SOURCES.txt +0 -0
- {sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/src/sharepoint_knowledge.egg-info/dependency_links.txt +0 -0
- {sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/src/sharepoint_knowledge.egg-info/entry_points.txt +0 -0
- {sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/src/sharepoint_knowledge.egg-info/top_level.txt +0 -0
- {sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/tests/test_client_sp.py +0 -0
- {sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/tests/test_crawl_sp.py +0 -0
- {sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/tests/test_server_flow_sp.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sharepoint-knowledge
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: SharePoint connector for the grounded-Q&A engine — site pages + document libraries, Entra device-code sign-in, local models, cited answers
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Project-URL: Homepage, https://github.com/nkunal1992/agents
|
|
@@ -11,7 +11,7 @@ Requires-Dist: msal>=1.31
|
|
|
11
11
|
Requires-Dist: httpx>=0.27
|
|
12
12
|
Requires-Dist: mcp>=1.8
|
|
13
13
|
Provides-Extra: models
|
|
14
|
-
Requires-Dist: confluence-knowledge[models]>=0.5.
|
|
14
|
+
Requires-Dist: confluence-knowledge[models]>=0.5.4; extra == "models"
|
|
15
15
|
Provides-Extra: dev
|
|
16
16
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
17
17
|
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sharepoint-knowledge"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.4"
|
|
8
8
|
description = "SharePoint connector for the grounded-Q&A engine — site pages + document libraries, Entra device-code sign-in, local models, cited answers"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -18,7 +18,7 @@ dependencies = [
|
|
|
18
18
|
|
|
19
19
|
[project.optional-dependencies]
|
|
20
20
|
models = [
|
|
21
|
-
"confluence-knowledge[models]>=0.5.
|
|
21
|
+
"confluence-knowledge[models]>=0.5.4",
|
|
22
22
|
]
|
|
23
23
|
dev = [
|
|
24
24
|
"pytest>=8.0",
|
{sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/src/sharepoint_knowledge/crawl_sp.py
RENAMED
|
@@ -197,6 +197,9 @@ class SPCrawler:
|
|
|
197
197
|
|
|
198
198
|
queue: list[str] = job["queue"]
|
|
199
199
|
errors: list[str] = job["errors"]
|
|
200
|
+
# deny/gone errors drop the item from the snapshot; transient
|
|
201
|
+
# failures keep the previously-indexed content
|
|
202
|
+
keep_failed: set[str] = set()
|
|
200
203
|
# Mid-crawl model change: already-embedded pages live in the OLD
|
|
201
204
|
# vector space — finishing would publish mixed dims. Wipe + fail;
|
|
202
205
|
# start_crawl rebuilds under the new model.
|
|
@@ -264,6 +267,13 @@ class SPCrawler:
|
|
|
264
267
|
continue
|
|
265
268
|
except AccessError as e:
|
|
266
269
|
errors.append(f"{entry}: {e}")
|
|
270
|
+
if not any(m in str(e) for m in
|
|
271
|
+
("401", "403", "404", "permission", "not found")):
|
|
272
|
+
# seen stores the built page_id — page: / file: prefixed
|
|
273
|
+
last = entry.split(":")[-1]
|
|
274
|
+
keep_failed.add(
|
|
275
|
+
f"page:{last}" if entry.startswith("page:")
|
|
276
|
+
else f"file:{last}")
|
|
267
277
|
continue
|
|
268
278
|
pid = page["page_id"]
|
|
269
279
|
if pid in seen:
|
|
@@ -285,7 +295,7 @@ class SPCrawler:
|
|
|
285
295
|
seen.add(pid)
|
|
286
296
|
|
|
287
297
|
self._checkpoint(job_id, queue, errors, seen, levels, warns)
|
|
288
|
-
self.index.publish(seen)
|
|
298
|
+
self.index.publish(seen | keep_failed)
|
|
289
299
|
self.index.set_model_id(_MODEL_ID)
|
|
290
300
|
self.index.save_job(job_id, "done" if not queue else "running",
|
|
291
301
|
queue, errors)
|
{sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/src/sharepoint_knowledge/server_sp.py
RENAMED
|
@@ -8,6 +8,7 @@ import logging
|
|
|
8
8
|
import threading
|
|
9
9
|
|
|
10
10
|
from confluence_knowledge.client import AccessError, SecurityException
|
|
11
|
+
from confluence_knowledge.feedback import FeedbackStore
|
|
11
12
|
from confluence_knowledge.index import Index
|
|
12
13
|
from confluence_knowledge.retrieval import (
|
|
13
14
|
MODEL_MARKER as EMBED_MODEL_ID,
|
|
@@ -30,8 +31,9 @@ from .settings_sp import APP_NAME, SPProfile, graph_base, home, index_root, work
|
|
|
30
31
|
mcp = FastMCP("sharepoint-knowledge")
|
|
31
32
|
|
|
32
33
|
_scopes: dict[str, dict] = {}
|
|
33
|
-
_evidence_sets: dict[str,
|
|
34
|
+
_evidence_sets: dict[str, dict] = {} # set_id → {scope, ev}
|
|
34
35
|
_indexes: dict[str, Index] = {}
|
|
36
|
+
_feedback: dict[str, FeedbackStore] = {}
|
|
35
37
|
_clients: dict[str, object] = {}
|
|
36
38
|
_EMBEDDER = None
|
|
37
39
|
_RERANKER = None
|
|
@@ -50,6 +52,13 @@ def _index_for(scope_key: str) -> Index:
|
|
|
50
52
|
idx = _indexes[scope_key] = Index(index_root() / f"{scope_key}.db")
|
|
51
53
|
return idx
|
|
52
54
|
|
|
55
|
+
def _feedback_for(scope_key: str) -> FeedbackStore:
|
|
56
|
+
st = _feedback.get(scope_key)
|
|
57
|
+
if st is None:
|
|
58
|
+
st = _feedback[scope_key] = FeedbackStore(
|
|
59
|
+
index_root(), scope_key, "sharepoint-knowledge")
|
|
60
|
+
return st
|
|
61
|
+
|
|
53
62
|
|
|
54
63
|
def _embedder():
|
|
55
64
|
global _EMBEDDER
|
|
@@ -309,17 +318,25 @@ def search(scope_handle: str, question: str) -> dict:
|
|
|
309
318
|
return {"evidence": [], "coverage": "stale_model",
|
|
310
319
|
"note": "this index was built with an older embedding model "
|
|
311
320
|
"— run start_crawl once to rebuild it, then search again"}
|
|
312
|
-
|
|
321
|
+
emb = _embedder()
|
|
322
|
+
ev = retrieve(idx, question, emb, _reranker(), sparse=_sparse())
|
|
323
|
+
corrections = _feedback_for(s["key"]).match(emb.embed([question])[0])
|
|
313
324
|
set_id = f"ev:{len(_evidence_sets)}:{s['key'][:8]}"
|
|
314
|
-
|
|
325
|
+
# bound the session cache — sets are never reused after the turn ends
|
|
326
|
+
while len(_evidence_sets) >= 32:
|
|
327
|
+
_evidence_sets.pop(next(iter(_evidence_sets)))
|
|
328
|
+
_evidence_sets[set_id] = {"scope": s["key"], "ev": ev}
|
|
315
329
|
return {
|
|
316
330
|
"evidence_set": set_id,
|
|
317
331
|
"coverage": _coverage(idx),
|
|
318
332
|
"as_of": st["as_of"],
|
|
333
|
+
"user_corrections": corrections,
|
|
319
334
|
"evidence": [
|
|
320
335
|
{"chunk_id": e["chunk_id"], "page_title": e["page_title"],
|
|
321
336
|
"page_url": e["page_url"], "heading": e["heading"],
|
|
322
|
-
"score": e.get("rerank_score"), "text": e["text"][:
|
|
337
|
+
"score": e.get("rerank_score"), "text": e["text"][:12000],
|
|
338
|
+
"low_confidence": e.get("low_confidence", False),
|
|
339
|
+
"truncated": len(e["text"]) > 12000}
|
|
323
340
|
for e in ev
|
|
324
341
|
],
|
|
325
342
|
}
|
|
@@ -342,15 +359,39 @@ def get_evidence(scope_handle: str, chunk_ids: list[str]) -> dict:
|
|
|
342
359
|
return {"chunks": out}
|
|
343
360
|
|
|
344
361
|
|
|
362
|
+
@mcp.tool()
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
@mcp.tool()
|
|
366
|
+
def submit_feedback(scope_handle: str, question: str, correction: str,
|
|
367
|
+
note: str = "") -> dict:
|
|
368
|
+
"""Store a colleague correction — disclosed alongside the docs on
|
|
369
|
+
similar future questions (never overrides them)."""
|
|
370
|
+
s = _scope(scope_handle)
|
|
371
|
+
if not _models_loaded(0):
|
|
372
|
+
return {"stored": False, "note": LOADING_NOTE}
|
|
373
|
+
qvec = _embedder().embed([question])[0]
|
|
374
|
+
entry = _feedback_for(s["key"]).add(question, qvec, correction, note)
|
|
375
|
+
return {"stored": entry["id"],
|
|
376
|
+
"note": "will be disclosed on similar questions"}
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
@mcp.tool()
|
|
380
|
+
def feedback_report(scope_handle: str) -> dict:
|
|
381
|
+
"""Aggregated corrections — which topics keep getting corrected points
|
|
382
|
+
at outdated/confusing documentation."""
|
|
383
|
+
s = _scope(scope_handle)
|
|
384
|
+
return _feedback_for(s["key"]).report()
|
|
385
|
+
|
|
345
386
|
@mcp.tool()
|
|
346
387
|
def verify_citations(scope_handle: str, evidence_set: str,
|
|
347
388
|
mappings: list[dict]) -> dict:
|
|
348
|
-
_scope(scope_handle)
|
|
349
|
-
|
|
350
|
-
if
|
|
389
|
+
s = _scope(scope_handle)
|
|
390
|
+
rec = _evidence_sets.get(evidence_set)
|
|
391
|
+
if rec is None or rec["scope"] != s["key"]:
|
|
351
392
|
raise SecurityException(
|
|
352
393
|
"unknown or cross-scope evidence set — run search again and cite its evidence_set")
|
|
353
|
-
return check_citations(ev, mappings)
|
|
394
|
+
return check_citations(rec["ev"], mappings)
|
|
354
395
|
|
|
355
396
|
|
|
356
397
|
def main() -> None:
|
{sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/src/sharepoint_knowledge.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sharepoint-knowledge
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: SharePoint connector for the grounded-Q&A engine — site pages + document libraries, Entra device-code sign-in, local models, cited answers
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Project-URL: Homepage, https://github.com/nkunal1992/agents
|
|
@@ -11,7 +11,7 @@ Requires-Dist: msal>=1.31
|
|
|
11
11
|
Requires-Dist: httpx>=0.27
|
|
12
12
|
Requires-Dist: mcp>=1.8
|
|
13
13
|
Provides-Extra: models
|
|
14
|
-
Requires-Dist: confluence-knowledge[models]>=0.5.
|
|
14
|
+
Requires-Dist: confluence-knowledge[models]>=0.5.4; extra == "models"
|
|
15
15
|
Provides-Extra: dev
|
|
16
16
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
17
17
|
Requires-Dist: ruff>=0.5; extra == "dev"
|
{sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/tests/test_agent_doc_consistency.py
RENAMED
|
@@ -27,3 +27,24 @@ def test_agent_references_repo_files_that_exist():
|
|
|
27
27
|
for m in re.findall(r"prompts/([\w.-]+)", doc):
|
|
28
28
|
assert (REPO / "prompts" / m).exists(), \
|
|
29
29
|
f"agent references missing prompts/{m}"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_agent_frontmatter_tools_exist():
|
|
33
|
+
"""Every tool named in the agent's frontmatter must be a real
|
|
34
|
+
@mcp.tool() in the server source."""
|
|
35
|
+
if not AGENT.exists():
|
|
36
|
+
import pytest
|
|
37
|
+
pytest.skip("agent file not in this checkout layout")
|
|
38
|
+
doc = AGENT.read_text()
|
|
39
|
+
tools_line = re.search(r'tools:\s*\[([^\]]+)\]', doc).group(1)
|
|
40
|
+
app = AGENT.stem.replace(".agent", "")
|
|
41
|
+
declared = {t.strip().strip('"').split("/")[1]
|
|
42
|
+
for t in tools_line.split(",") if f"{app}/" in t}
|
|
43
|
+
srv_name = {"local-knowledge": "server_local",
|
|
44
|
+
"sharepoint-knowledge": "server_sp",
|
|
45
|
+
"confluence-knowledge": "server"}[app]
|
|
46
|
+
pkg = app.replace("-", "_")
|
|
47
|
+
srv = (Path(__file__).parents[1] / "src" / pkg / f"{srv_name}.py").read_text()
|
|
48
|
+
real = set(re.findall(r"@mcp\.tool\(\)\s*\ndef\s+(\w+)", srv))
|
|
49
|
+
missing = declared - real
|
|
50
|
+
assert not missing, f"agent frontmatter names tools not in server: {missing}"
|
|
File without changes
|
|
File without changes
|
{sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/src/sharepoint_knowledge/__init__.py
RENAMED
|
File without changes
|
{sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/src/sharepoint_knowledge/auth_sp.py
RENAMED
|
File without changes
|
{sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/src/sharepoint_knowledge/cli_sp.py
RENAMED
|
File without changes
|
{sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/src/sharepoint_knowledge/client_sp.py
RENAMED
|
File without changes
|
{sharepoint_knowledge-0.2.2 → sharepoint_knowledge-0.2.4}/src/sharepoint_knowledge/settings_sp.py
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
|