sharepoint-knowledge 0.1.0__tar.gz → 0.2.1__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.1.0 → sharepoint_knowledge-0.2.1}/PKG-INFO +3 -3
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/pyproject.toml +3 -3
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/src/sharepoint_knowledge/cli_sp.py +12 -4
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/src/sharepoint_knowledge/crawl_sp.py +11 -5
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/src/sharepoint_knowledge/server_sp.py +33 -6
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/src/sharepoint_knowledge.egg-info/PKG-INFO +3 -3
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/src/sharepoint_knowledge.egg-info/requires.txt +2 -2
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/tests/test_client_sp.py +0 -1
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/tests/test_crawl_sp.py +0 -1
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/tests/test_server_flow_sp.py +6 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/README.md +0 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/setup.cfg +0 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/src/sharepoint_knowledge/__init__.py +0 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/src/sharepoint_knowledge/auth_sp.py +0 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/src/sharepoint_knowledge/client_sp.py +0 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/src/sharepoint_knowledge/settings_sp.py +0 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/src/sharepoint_knowledge.egg-info/SOURCES.txt +0 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/src/sharepoint_knowledge.egg-info/dependency_links.txt +0 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/src/sharepoint_knowledge.egg-info/entry_points.txt +0 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/src/sharepoint_knowledge.egg-info/top_level.txt +0 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/tests/test_agent_doc_consistency.py +0 -0
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sharepoint-knowledge
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
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
|
|
7
7
|
Requires-Python: >=3.11
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
|
-
Requires-Dist: confluence-knowledge[files]>=0.
|
|
9
|
+
Requires-Dist: confluence-knowledge[files]>=0.5.1
|
|
10
10
|
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.
|
|
14
|
+
Requires-Dist: confluence-knowledge[models]>=0.5.1; 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,13 +4,13 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sharepoint-knowledge"
|
|
7
|
-
version = "0.1
|
|
7
|
+
version = "0.2.1"
|
|
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"
|
|
11
11
|
license = {text = "Apache-2.0"}
|
|
12
12
|
dependencies = [
|
|
13
|
-
"confluence-knowledge[files]>=0.
|
|
13
|
+
"confluence-knowledge[files]>=0.5.1",
|
|
14
14
|
"msal>=1.31",
|
|
15
15
|
"httpx>=0.27",
|
|
16
16
|
"mcp>=1.8",
|
|
@@ -18,7 +18,7 @@ dependencies = [
|
|
|
18
18
|
|
|
19
19
|
[project.optional-dependencies]
|
|
20
20
|
models = [
|
|
21
|
-
"confluence-knowledge[models]>=0.
|
|
21
|
+
"confluence-knowledge[models]>=0.5.1",
|
|
22
22
|
]
|
|
23
23
|
dev = [
|
|
24
24
|
"pytest>=8.0",
|
{sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/src/sharepoint_knowledge/cli_sp.py
RENAMED
|
@@ -84,7 +84,7 @@ def _setup() -> int:
|
|
|
84
84
|
ensure_gitignore(workdir(), [AUTH_FILE_NAME,
|
|
85
85
|
f"{INDEX_DIR_NAME}/"])
|
|
86
86
|
|
|
87
|
-
if input("\nDownload the
|
|
87
|
+
if input("\nDownload the local models now (~1.3 GB, one-time)? "
|
|
88
88
|
"[Y/n] ").strip().lower() not in ("n", "no"):
|
|
89
89
|
for line in prepare_models(home()):
|
|
90
90
|
print(line)
|
|
@@ -99,6 +99,13 @@ def _doctor() -> int:
|
|
|
99
99
|
from .server_sp import status
|
|
100
100
|
s = status()
|
|
101
101
|
print(json.dumps(s, indent=2))
|
|
102
|
+
bad = [c for c in s.get("checks", []) if not c["ok"]]
|
|
103
|
+
if bad:
|
|
104
|
+
print("\nhealth flags:")
|
|
105
|
+
for c in bad:
|
|
106
|
+
print(f" ✗ {c['flag']}: {c['detail']}")
|
|
107
|
+
if c.get("remediation"):
|
|
108
|
+
print(f" → {c['remediation']}")
|
|
102
109
|
upd = check("sharepoint-knowledge")
|
|
103
110
|
if upd["installed"]:
|
|
104
111
|
line = f"version: {upd['installed']}"
|
|
@@ -110,7 +117,7 @@ def _doctor() -> int:
|
|
|
110
117
|
if upd["update_available"]:
|
|
111
118
|
print("update available: run `sharepoint-knowledge update` "
|
|
112
119
|
"(or pip install -U sharepoint-knowledge[models])")
|
|
113
|
-
return 0
|
|
120
|
+
return 4 if bad else 0
|
|
114
121
|
|
|
115
122
|
|
|
116
123
|
def main() -> None:
|
|
@@ -152,8 +159,9 @@ def main() -> None:
|
|
|
152
159
|
return
|
|
153
160
|
r = subprocess.run(cmd, check=False)
|
|
154
161
|
if r.returncode == 0:
|
|
155
|
-
print("\nupdated —
|
|
156
|
-
"
|
|
162
|
+
print("\nupdated — run `sharepoint-knowledge prepare-models` "
|
|
163
|
+
"(new releases may pin new model artifacts) then "
|
|
164
|
+
"`sharepoint-knowledge doctor`")
|
|
157
165
|
sys.exit(r.returncode)
|
|
158
166
|
if args.cmd == "register":
|
|
159
167
|
results = _register(
|
{sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/src/sharepoint_knowledge/crawl_sp.py
RENAMED
|
@@ -23,6 +23,8 @@ from confluence_knowledge.settings import SetupError
|
|
|
23
23
|
MAX_FILE_BYTES = 25 * 1024 * 1024
|
|
24
24
|
MAX_FILES = 20_000
|
|
25
25
|
|
|
26
|
+
from confluence_knowledge.retrieval import EMBED_MODEL_ID as _MODEL_ID
|
|
27
|
+
|
|
26
28
|
|
|
27
29
|
def resolve_root(client: ConfluenceClient, url: str) -> tuple[str, str]:
|
|
28
30
|
"""SharePoint URL → (root queue entry, site webUrl for citations).
|
|
@@ -77,10 +79,11 @@ def _paged(client: ConfluenceClient, path: str, params: dict | None = None):
|
|
|
77
79
|
|
|
78
80
|
class SPCrawler:
|
|
79
81
|
def __init__(self, client: ConfluenceClient, index: Index, embedder=None,
|
|
80
|
-
downloader=None):
|
|
82
|
+
downloader=None, sparse=None):
|
|
81
83
|
self.client = client
|
|
82
84
|
self.index = index
|
|
83
85
|
self.embedder = embedder
|
|
86
|
+
self.sparse = sparse
|
|
84
87
|
self.downloader = downloader # injected for tests; real one in server
|
|
85
88
|
|
|
86
89
|
# ---- expansions ----
|
|
@@ -255,20 +258,23 @@ class SPCrawler:
|
|
|
255
258
|
continue
|
|
256
259
|
for c in chunks:
|
|
257
260
|
c.chunk_id = f"{pid}:{page['content_hash'][:8]}:{c.ordinal}"
|
|
258
|
-
vecs = None
|
|
261
|
+
vecs, sparse_vecs = None, None
|
|
259
262
|
if self.embedder is not None and chunks:
|
|
260
|
-
|
|
261
|
-
|
|
263
|
+
texts = [f"{c.heading_path}\n{c.text}" for c in chunks]
|
|
264
|
+
vecs = self.embedder.embed(texts)
|
|
265
|
+
if self.sparse is not None:
|
|
266
|
+
sparse_vecs = self.sparse.embed(texts)
|
|
262
267
|
self.index.upsert_page({
|
|
263
268
|
"page_id": pid, "parent_id": None, "title": page["title"],
|
|
264
269
|
"url": page["url"], "level": levels.get(pid, 0),
|
|
265
270
|
"version": page["version"],
|
|
266
271
|
"content_hash": page["content_hash"],
|
|
267
|
-
"warnings": page["warnings"]}, chunks, vecs)
|
|
272
|
+
"warnings": page["warnings"]}, chunks, vecs, sparse_vecs)
|
|
268
273
|
seen.add(pid)
|
|
269
274
|
|
|
270
275
|
self._checkpoint(job_id, queue, errors, seen, levels, warns)
|
|
271
276
|
self.index.publish(seen)
|
|
277
|
+
self.index.set_model_id(_MODEL_ID)
|
|
272
278
|
self.index.save_job(job_id, "done" if not queue else "running",
|
|
273
279
|
queue, errors)
|
|
274
280
|
return {"job_id": job_id,
|
{sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/src/sharepoint_knowledge/server_sp.py
RENAMED
|
@@ -10,8 +10,10 @@ import threading
|
|
|
10
10
|
from confluence_knowledge.client import AccessError, SecurityException
|
|
11
11
|
from confluence_knowledge.index import Index
|
|
12
12
|
from confluence_knowledge.retrieval import (
|
|
13
|
+
EMBED_MODEL_ID,
|
|
13
14
|
LocalEmbedder,
|
|
14
15
|
LocalReranker,
|
|
16
|
+
LocalSparseEmbedder,
|
|
15
17
|
check_citations,
|
|
16
18
|
retrieve,
|
|
17
19
|
)
|
|
@@ -21,7 +23,7 @@ from mcp.server.fastmcp import FastMCP
|
|
|
21
23
|
from .auth_sp import acquire_token
|
|
22
24
|
from .client_sp import download_file, make_client
|
|
23
25
|
from .crawl_sp import SPCrawler, resolve_root
|
|
24
|
-
from .settings_sp import APP_NAME, SPProfile, graph_base, home, index_root
|
|
26
|
+
from .settings_sp import APP_NAME, SPProfile, graph_base, home, index_root, workdir
|
|
25
27
|
|
|
26
28
|
mcp = FastMCP("sharepoint-knowledge")
|
|
27
29
|
|
|
@@ -31,6 +33,7 @@ _indexes: dict[str, Index] = {}
|
|
|
31
33
|
_clients: dict[str, object] = {}
|
|
32
34
|
_EMBEDDER = None
|
|
33
35
|
_RERANKER = None
|
|
36
|
+
_SPARSE = None
|
|
34
37
|
_model_lock = threading.Lock()
|
|
35
38
|
_models_ready = threading.Event()
|
|
36
39
|
_warm_lock = threading.Lock()
|
|
@@ -62,10 +65,19 @@ def _reranker():
|
|
|
62
65
|
return _RERANKER
|
|
63
66
|
|
|
64
67
|
|
|
68
|
+
def _sparse():
|
|
69
|
+
global _SPARSE
|
|
70
|
+
with _model_lock:
|
|
71
|
+
if _SPARSE is None:
|
|
72
|
+
_SPARSE = LocalSparseEmbedder(home(), APP_NAME)
|
|
73
|
+
return _SPARSE
|
|
74
|
+
|
|
75
|
+
|
|
65
76
|
def _warm_models() -> None:
|
|
66
77
|
try:
|
|
67
78
|
_embedder()
|
|
68
79
|
_reranker()
|
|
80
|
+
_sparse()
|
|
69
81
|
except SetupError:
|
|
70
82
|
pass
|
|
71
83
|
finally:
|
|
@@ -114,7 +126,6 @@ def _scope(scope_handle: str) -> dict:
|
|
|
114
126
|
@mcp.tool()
|
|
115
127
|
def status() -> dict:
|
|
116
128
|
"""Report setup readiness — profile present, token reachable, models."""
|
|
117
|
-
models = home() / "models"
|
|
118
129
|
try:
|
|
119
130
|
profile = SPProfile.load()
|
|
120
131
|
try:
|
|
@@ -137,11 +148,19 @@ def status() -> dict:
|
|
|
137
148
|
except AccessError as e:
|
|
138
149
|
cfg["auth"] = str(e)
|
|
139
150
|
configured = False
|
|
151
|
+
from confluence_knowledge.health import health_checks
|
|
152
|
+
checks = health_checks(home(), workdir(), APP_NAME, "sharepoint-knowledge",
|
|
153
|
+
index_root=index_root())
|
|
154
|
+
checks.append({
|
|
155
|
+
"flag": "creds_valid", "ok": configured,
|
|
156
|
+
"detail": cfg.get("auth") if configured
|
|
157
|
+
else cfg.get("auth") or "no profile",
|
|
158
|
+
"remediation": None if configured
|
|
159
|
+
else "sharepoint-knowledge setup (re-sign-in)"})
|
|
140
160
|
return {
|
|
141
161
|
"configured": configured,
|
|
142
162
|
"profile": cfg,
|
|
143
|
-
"
|
|
144
|
-
"reranker_model_ready": (models / "ms-marco-MiniLM-L6-v2").exists(),
|
|
163
|
+
"checks": checks,
|
|
145
164
|
"models_loaded": _models_ready.is_set() and _EMBEDDER is not None,
|
|
146
165
|
"index_root": str(index_root()),
|
|
147
166
|
"live_scopes": sorted(_scopes),
|
|
@@ -166,6 +185,7 @@ def connect(root: str) -> dict:
|
|
|
166
185
|
_scopes[handle] = {"key": scope_key, "root_entry": root_entry,
|
|
167
186
|
"site_web": site_web, "client": cli}
|
|
168
187
|
_start_warmup()
|
|
188
|
+
stale = idx.model_id() is not None and idx.model_id() != EMBED_MODEL_ID
|
|
169
189
|
return {
|
|
170
190
|
"scope_handle": handle,
|
|
171
191
|
"root": root_entry,
|
|
@@ -173,6 +193,9 @@ def connect(root: str) -> dict:
|
|
|
173
193
|
"cached": st["pages"] > 0,
|
|
174
194
|
"cached_pages": st["pages"],
|
|
175
195
|
"cached_as_of": st["as_of"],
|
|
196
|
+
"stale_model": stale,
|
|
197
|
+
"note": ("index was built with an older model — run start_crawl once "
|
|
198
|
+
"to rebuild it with the current models" if stale else None),
|
|
176
199
|
}
|
|
177
200
|
|
|
178
201
|
|
|
@@ -186,7 +209,7 @@ def _worker_loop(s: dict, job_id: str) -> None:
|
|
|
186
209
|
try:
|
|
187
210
|
_models_ready.wait()
|
|
188
211
|
c = SPCrawler(s["client"], idx, embedder=_embedder(),
|
|
189
|
-
downloader=download_file)
|
|
212
|
+
downloader=download_file, sparse=_sparse())
|
|
190
213
|
r = c.step(job_id)
|
|
191
214
|
while r["state"] == "running":
|
|
192
215
|
r = c.step(job_id)
|
|
@@ -280,7 +303,11 @@ def search(scope_handle: str, question: str) -> dict:
|
|
|
280
303
|
"note": "nothing indexed — run a crawl first"}
|
|
281
304
|
if not _models_loaded(10.0):
|
|
282
305
|
return {"evidence": [], "coverage": "loading", "note": LOADING_NOTE}
|
|
283
|
-
|
|
306
|
+
if idx.model_id() is not None and idx.model_id() != EMBED_MODEL_ID:
|
|
307
|
+
return {"evidence": [], "coverage": "stale_model",
|
|
308
|
+
"note": "this index was built with an older embedding model "
|
|
309
|
+
"— run start_crawl once to rebuild it, then search again"}
|
|
310
|
+
ev = retrieve(idx, question, _embedder(), _reranker(), sparse=_sparse())
|
|
284
311
|
set_id = f"ev:{len(_evidence_sets)}:{s['key'][:8]}"
|
|
285
312
|
_evidence_sets[set_id] = ev
|
|
286
313
|
return {
|
{sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/src/sharepoint_knowledge.egg-info/PKG-INFO
RENAMED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sharepoint-knowledge
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
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
|
|
7
7
|
Requires-Python: >=3.11
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
|
-
Requires-Dist: confluence-knowledge[files]>=0.
|
|
9
|
+
Requires-Dist: confluence-knowledge[files]>=0.5.1
|
|
10
10
|
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.
|
|
14
|
+
Requires-Dist: confluence-knowledge[models]>=0.5.1; 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"
|
|
@@ -25,6 +25,11 @@ class FakeReranker:
|
|
|
25
25
|
return np.arange(len(docs), dtype=np.float32)[::-1]
|
|
26
26
|
|
|
27
27
|
|
|
28
|
+
class FakeSparse:
|
|
29
|
+
def embed(self, texts):
|
|
30
|
+
return [{} for _ in texts]
|
|
31
|
+
|
|
32
|
+
|
|
28
33
|
@pytest.fixture()
|
|
29
34
|
def sp_env(tmp_path, monkeypatch):
|
|
30
35
|
monkeypatch.setenv("SP_KNOWLEDGE_HOME", str(tmp_path / "home"))
|
|
@@ -40,6 +45,7 @@ def sp_env(tmp_path, monkeypatch):
|
|
|
40
45
|
|
|
41
46
|
server_sp._EMBEDDER = FakeEmbedder()
|
|
42
47
|
server_sp._RERANKER = FakeReranker()
|
|
48
|
+
server_sp._SPARSE = FakeSparse()
|
|
43
49
|
server_sp._models_ready.set()
|
|
44
50
|
|
|
45
51
|
# the real client goes through httpx — replace it with the fake
|
|
File without changes
|
|
File without changes
|
{sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/src/sharepoint_knowledge/__init__.py
RENAMED
|
File without changes
|
{sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/src/sharepoint_knowledge/auth_sp.py
RENAMED
|
File without changes
|
{sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/src/sharepoint_knowledge/client_sp.py
RENAMED
|
File without changes
|
{sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/src/sharepoint_knowledge/settings_sp.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.1}/tests/test_agent_doc_consistency.py
RENAMED
|
File without changes
|