sharepoint-knowledge 0.2.1__tar.gz → 0.2.3__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 (21) hide show
  1. {sharepoint_knowledge-0.2.1 → sharepoint_knowledge-0.2.3}/PKG-INFO +2 -2
  2. {sharepoint_knowledge-0.2.1 → sharepoint_knowledge-0.2.3}/pyproject.toml +2 -2
  3. {sharepoint_knowledge-0.2.1 → sharepoint_knowledge-0.2.3}/src/sharepoint_knowledge/cli_sp.py +3 -0
  4. {sharepoint_knowledge-0.2.1 → sharepoint_knowledge-0.2.3}/src/sharepoint_knowledge/crawl_sp.py +24 -2
  5. {sharepoint_knowledge-0.2.1 → sharepoint_knowledge-0.2.3}/src/sharepoint_knowledge/server_sp.py +15 -8
  6. {sharepoint_knowledge-0.2.1 → sharepoint_knowledge-0.2.3}/src/sharepoint_knowledge.egg-info/PKG-INFO +2 -2
  7. {sharepoint_knowledge-0.2.1 → sharepoint_knowledge-0.2.3}/src/sharepoint_knowledge.egg-info/requires.txt +1 -1
  8. {sharepoint_knowledge-0.2.1 → sharepoint_knowledge-0.2.3}/README.md +0 -0
  9. {sharepoint_knowledge-0.2.1 → sharepoint_knowledge-0.2.3}/setup.cfg +0 -0
  10. {sharepoint_knowledge-0.2.1 → sharepoint_knowledge-0.2.3}/src/sharepoint_knowledge/__init__.py +0 -0
  11. {sharepoint_knowledge-0.2.1 → sharepoint_knowledge-0.2.3}/src/sharepoint_knowledge/auth_sp.py +0 -0
  12. {sharepoint_knowledge-0.2.1 → sharepoint_knowledge-0.2.3}/src/sharepoint_knowledge/client_sp.py +0 -0
  13. {sharepoint_knowledge-0.2.1 → sharepoint_knowledge-0.2.3}/src/sharepoint_knowledge/settings_sp.py +0 -0
  14. {sharepoint_knowledge-0.2.1 → sharepoint_knowledge-0.2.3}/src/sharepoint_knowledge.egg-info/SOURCES.txt +0 -0
  15. {sharepoint_knowledge-0.2.1 → sharepoint_knowledge-0.2.3}/src/sharepoint_knowledge.egg-info/dependency_links.txt +0 -0
  16. {sharepoint_knowledge-0.2.1 → sharepoint_knowledge-0.2.3}/src/sharepoint_knowledge.egg-info/entry_points.txt +0 -0
  17. {sharepoint_knowledge-0.2.1 → sharepoint_knowledge-0.2.3}/src/sharepoint_knowledge.egg-info/top_level.txt +0 -0
  18. {sharepoint_knowledge-0.2.1 → sharepoint_knowledge-0.2.3}/tests/test_agent_doc_consistency.py +0 -0
  19. {sharepoint_knowledge-0.2.1 → sharepoint_knowledge-0.2.3}/tests/test_client_sp.py +0 -0
  20. {sharepoint_knowledge-0.2.1 → sharepoint_knowledge-0.2.3}/tests/test_crawl_sp.py +0 -0
  21. {sharepoint_knowledge-0.2.1 → sharepoint_knowledge-0.2.3}/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.1
3
+ Version: 0.2.3
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.1; extra == "models"
14
+ Requires-Dist: confluence-knowledge[models]>=0.5.3; 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.1"
7
+ version = "0.2.3"
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.1",
21
+ "confluence-knowledge[models]>=0.5.3",
22
22
  ]
23
23
  dev = [
24
24
  "pytest>=8.0",
@@ -153,6 +153,9 @@ def main() -> None:
153
153
  import subprocess
154
154
  cmd = [sys.executable, "-m", "pip", "install", "-U",
155
155
  "sharepoint-knowledge[models]"]
156
+ # a running MCP server holds open files — quit the IDE first
157
+ print("note: quit the IDE (or stop the MCP server) before updating —"
158
+ " a running server can't be replaced cleanly", flush=True)
156
159
  if os.name == "nt":
157
160
  print("on Windows, upgrade via the venv python directly:\n "
158
161
  + " ".join(f'"{c}"' if " " in c else c for c in cmd))
@@ -23,7 +23,7 @@ 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
26
+ from confluence_knowledge.retrieval import MODEL_MARKER as _MODEL_ID
27
27
 
28
28
 
29
29
  def resolve_root(client: ConfluenceClient, url: str) -> tuple[str, str]:
@@ -197,6 +197,21 @@ 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()
203
+ # Mid-crawl model change: already-embedded pages live in the OLD
204
+ # vector space — finishing would publish mixed dims. Wipe + fail;
205
+ # start_crawl rebuilds under the new model.
206
+ stamped = self.index.model_id()
207
+ if stamped is not None and stamped != _MODEL_ID and queue:
208
+ self.index.clear_scope()
209
+ errors = errors + [
210
+ (f"model changed mid-crawl ({stamped} → {_MODEL_ID}) — "
211
+ "index cleared; run start_crawl")]
212
+ self.index.save_job(job_id, "error", [], errors)
213
+ return {"job_id": job_id, "state": "error",
214
+ "remaining": 0, "errors": errors}
200
215
  seen_raw = self.index.meta_get(f"seen:{job_id}")
201
216
  seen: set[str] = set(json.loads(seen_raw)) if seen_raw else set()
202
217
  levels_raw = self.index.meta_get(f"levels:{job_id}")
@@ -252,6 +267,13 @@ class SPCrawler:
252
267
  continue
253
268
  except AccessError as e:
254
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}")
255
277
  continue
256
278
  pid = page["page_id"]
257
279
  if pid in seen:
@@ -273,7 +295,7 @@ class SPCrawler:
273
295
  seen.add(pid)
274
296
 
275
297
  self._checkpoint(job_id, queue, errors, seen, levels, warns)
276
- self.index.publish(seen)
298
+ self.index.publish(seen | keep_failed)
277
299
  self.index.set_model_id(_MODEL_ID)
278
300
  self.index.save_job(job_id, "done" if not queue else "running",
279
301
  queue, errors)
@@ -10,7 +10,9 @@ 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
+ MODEL_MARKER as EMBED_MODEL_ID,
14
+ )
15
+ from confluence_knowledge.retrieval import (
14
16
  LocalEmbedder,
15
17
  LocalReranker,
16
18
  LocalSparseEmbedder,
@@ -28,7 +30,7 @@ from .settings_sp import APP_NAME, SPProfile, graph_base, home, index_root, work
28
30
  mcp = FastMCP("sharepoint-knowledge")
29
31
 
30
32
  _scopes: dict[str, dict] = {}
31
- _evidence_sets: dict[str, list[dict]] = {}
33
+ _evidence_sets: dict[str, dict] = {} # set_id → {scope, ev}
32
34
  _indexes: dict[str, Index] = {}
33
35
  _clients: dict[str, object] = {}
34
36
  _EMBEDDER = None
@@ -309,7 +311,10 @@ def search(scope_handle: str, question: str) -> dict:
309
311
  "— run start_crawl once to rebuild it, then search again"}
310
312
  ev = retrieve(idx, question, _embedder(), _reranker(), sparse=_sparse())
311
313
  set_id = f"ev:{len(_evidence_sets)}:{s['key'][:8]}"
312
- _evidence_sets[set_id] = ev
314
+ # bound the session cache — sets are never reused after the turn ends
315
+ while len(_evidence_sets) >= 32:
316
+ _evidence_sets.pop(next(iter(_evidence_sets)))
317
+ _evidence_sets[set_id] = {"scope": s["key"], "ev": ev}
313
318
  return {
314
319
  "evidence_set": set_id,
315
320
  "coverage": _coverage(idx),
@@ -317,7 +322,9 @@ def search(scope_handle: str, question: str) -> dict:
317
322
  "evidence": [
318
323
  {"chunk_id": e["chunk_id"], "page_title": e["page_title"],
319
324
  "page_url": e["page_url"], "heading": e["heading"],
320
- "score": e.get("rerank_score"), "text": e["text"][:2000]}
325
+ "score": e.get("rerank_score"), "text": e["text"][:12000],
326
+ "low_confidence": e.get("low_confidence", False),
327
+ "truncated": len(e["text"]) > 12000}
321
328
  for e in ev
322
329
  ],
323
330
  }
@@ -343,12 +350,12 @@ def get_evidence(scope_handle: str, chunk_ids: list[str]) -> dict:
343
350
  @mcp.tool()
344
351
  def verify_citations(scope_handle: str, evidence_set: str,
345
352
  mappings: list[dict]) -> dict:
346
- _scope(scope_handle)
347
- ev = _evidence_sets.get(evidence_set)
348
- if ev is None or not evidence_set.endswith(_scopes[scope_handle]["key"][:8]):
353
+ s = _scope(scope_handle)
354
+ rec = _evidence_sets.get(evidence_set)
355
+ if rec is None or rec["scope"] != s["key"]:
349
356
  raise SecurityException(
350
357
  "unknown or cross-scope evidence set — run search again and cite its evidence_set")
351
- return check_citations(ev, mappings)
358
+ return check_citations(rec["ev"], mappings)
352
359
 
353
360
 
354
361
  def main() -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sharepoint-knowledge
3
- Version: 0.2.1
3
+ Version: 0.2.3
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.1; extra == "models"
14
+ Requires-Dist: confluence-knowledge[models]>=0.5.3; 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"
@@ -12,4 +12,4 @@ openpyxl>=3.1
12
12
  pypdf>=5.0
13
13
 
14
14
  [models]
15
- confluence-knowledge[models]>=0.5.1
15
+ confluence-knowledge[models]>=0.5.3