sharepoint-knowledge 0.1.0__tar.gz → 0.2.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.
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/PKG-INFO +3 -3
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/pyproject.toml +3 -3
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/src/sharepoint_knowledge/cli_sp.py +4 -3
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/src/sharepoint_knowledge/crawl_sp.py +11 -5
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/src/sharepoint_knowledge/server_sp.py +26 -4
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/src/sharepoint_knowledge.egg-info/PKG-INFO +3 -3
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/src/sharepoint_knowledge.egg-info/requires.txt +2 -2
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/tests/test_client_sp.py +0 -1
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/tests/test_crawl_sp.py +0 -1
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/tests/test_server_flow_sp.py +6 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/README.md +0 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/setup.cfg +0 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/src/sharepoint_knowledge/__init__.py +0 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/src/sharepoint_knowledge/auth_sp.py +0 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/src/sharepoint_knowledge/client_sp.py +0 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/src/sharepoint_knowledge/settings_sp.py +0 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/src/sharepoint_knowledge.egg-info/SOURCES.txt +0 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/src/sharepoint_knowledge.egg-info/dependency_links.txt +0 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/src/sharepoint_knowledge.egg-info/entry_points.txt +0 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/src/sharepoint_knowledge.egg-info/top_level.txt +0 -0
- {sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/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.
|
|
3
|
+
Version: 0.2.0
|
|
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.0
|
|
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.0; 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.
|
|
7
|
+
version = "0.2.0"
|
|
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.0",
|
|
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.0",
|
|
22
22
|
]
|
|
23
23
|
dev = [
|
|
24
24
|
"pytest>=8.0",
|
{sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/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)
|
|
@@ -152,8 +152,9 @@ def main() -> None:
|
|
|
152
152
|
return
|
|
153
153
|
r = subprocess.run(cmd, check=False)
|
|
154
154
|
if r.returncode == 0:
|
|
155
|
-
print("\nupdated —
|
|
156
|
-
"
|
|
155
|
+
print("\nupdated — run `sharepoint-knowledge prepare-models` "
|
|
156
|
+
"(new releases may pin new model artifacts) then "
|
|
157
|
+
"`sharepoint-knowledge doctor`")
|
|
157
158
|
sys.exit(r.returncode)
|
|
158
159
|
if args.cmd == "register":
|
|
159
160
|
results = _register(
|
{sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/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.0}/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
|
)
|
|
@@ -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:
|
|
@@ -140,8 +152,10 @@ def status() -> dict:
|
|
|
140
152
|
return {
|
|
141
153
|
"configured": configured,
|
|
142
154
|
"profile": cfg,
|
|
143
|
-
"embedding_model_ready": (models / "
|
|
144
|
-
"reranker_model_ready": (models / "
|
|
155
|
+
"embedding_model_ready": (models / "bge-m3").exists(),
|
|
156
|
+
"reranker_model_ready": (models / "bge-reranker-v2-m3-ONNX").exists()
|
|
157
|
+
or (models / "bge-reranker-base-ONNX").exists(),
|
|
158
|
+
"sparse_model_ready": (models / "fastembed").exists(),
|
|
145
159
|
"models_loaded": _models_ready.is_set() and _EMBEDDER is not None,
|
|
146
160
|
"index_root": str(index_root()),
|
|
147
161
|
"live_scopes": sorted(_scopes),
|
|
@@ -166,6 +180,7 @@ def connect(root: str) -> dict:
|
|
|
166
180
|
_scopes[handle] = {"key": scope_key, "root_entry": root_entry,
|
|
167
181
|
"site_web": site_web, "client": cli}
|
|
168
182
|
_start_warmup()
|
|
183
|
+
stale = idx.model_id() is not None and idx.model_id() != EMBED_MODEL_ID
|
|
169
184
|
return {
|
|
170
185
|
"scope_handle": handle,
|
|
171
186
|
"root": root_entry,
|
|
@@ -173,6 +188,9 @@ def connect(root: str) -> dict:
|
|
|
173
188
|
"cached": st["pages"] > 0,
|
|
174
189
|
"cached_pages": st["pages"],
|
|
175
190
|
"cached_as_of": st["as_of"],
|
|
191
|
+
"stale_model": stale,
|
|
192
|
+
"note": ("index was built with an older model — run start_crawl once "
|
|
193
|
+
"to rebuild it with the current models" if stale else None),
|
|
176
194
|
}
|
|
177
195
|
|
|
178
196
|
|
|
@@ -186,7 +204,7 @@ def _worker_loop(s: dict, job_id: str) -> None:
|
|
|
186
204
|
try:
|
|
187
205
|
_models_ready.wait()
|
|
188
206
|
c = SPCrawler(s["client"], idx, embedder=_embedder(),
|
|
189
|
-
downloader=download_file)
|
|
207
|
+
downloader=download_file, sparse=_sparse())
|
|
190
208
|
r = c.step(job_id)
|
|
191
209
|
while r["state"] == "running":
|
|
192
210
|
r = c.step(job_id)
|
|
@@ -280,7 +298,11 @@ def search(scope_handle: str, question: str) -> dict:
|
|
|
280
298
|
"note": "nothing indexed — run a crawl first"}
|
|
281
299
|
if not _models_loaded(10.0):
|
|
282
300
|
return {"evidence": [], "coverage": "loading", "note": LOADING_NOTE}
|
|
283
|
-
|
|
301
|
+
if idx.model_id() is not None and idx.model_id() != EMBED_MODEL_ID:
|
|
302
|
+
return {"evidence": [], "coverage": "stale_model",
|
|
303
|
+
"note": "this index was built with an older embedding model "
|
|
304
|
+
"— run start_crawl once to rebuild it, then search again"}
|
|
305
|
+
ev = retrieve(idx, question, _embedder(), _reranker(), sparse=_sparse())
|
|
284
306
|
set_id = f"ev:{len(_evidence_sets)}:{s['key'][:8]}"
|
|
285
307
|
_evidence_sets[set_id] = ev
|
|
286
308
|
return {
|
{sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/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.
|
|
3
|
+
Version: 0.2.0
|
|
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.0
|
|
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.0; 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.0}/src/sharepoint_knowledge/__init__.py
RENAMED
|
File without changes
|
{sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/src/sharepoint_knowledge/auth_sp.py
RENAMED
|
File without changes
|
{sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/src/sharepoint_knowledge/client_sp.py
RENAMED
|
File without changes
|
{sharepoint_knowledge-0.1.0 → sharepoint_knowledge-0.2.0}/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.0}/tests/test_agent_doc_consistency.py
RENAMED
|
File without changes
|