local-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.
- {local_knowledge-0.2.1 → local_knowledge-0.2.3}/PKG-INFO +2 -2
- {local_knowledge-0.2.1 → local_knowledge-0.2.3}/pyproject.toml +2 -2
- {local_knowledge-0.2.1 → local_knowledge-0.2.3}/src/local_knowledge/cli_local.py +3 -0
- {local_knowledge-0.2.1 → local_knowledge-0.2.3}/src/local_knowledge/crawl_local.py +16 -2
- {local_knowledge-0.2.1 → local_knowledge-0.2.3}/src/local_knowledge/server_local.py +15 -8
- {local_knowledge-0.2.1 → local_knowledge-0.2.3}/src/local_knowledge.egg-info/PKG-INFO +2 -2
- {local_knowledge-0.2.1 → local_knowledge-0.2.3}/src/local_knowledge.egg-info/requires.txt +1 -1
- {local_knowledge-0.2.1 → local_knowledge-0.2.3}/README.md +0 -0
- {local_knowledge-0.2.1 → local_knowledge-0.2.3}/setup.cfg +0 -0
- {local_knowledge-0.2.1 → local_knowledge-0.2.3}/src/local_knowledge/__init__.py +0 -0
- {local_knowledge-0.2.1 → local_knowledge-0.2.3}/src/local_knowledge/settings_local.py +0 -0
- {local_knowledge-0.2.1 → local_knowledge-0.2.3}/src/local_knowledge.egg-info/SOURCES.txt +0 -0
- {local_knowledge-0.2.1 → local_knowledge-0.2.3}/src/local_knowledge.egg-info/dependency_links.txt +0 -0
- {local_knowledge-0.2.1 → local_knowledge-0.2.3}/src/local_knowledge.egg-info/entry_points.txt +0 -0
- {local_knowledge-0.2.1 → local_knowledge-0.2.3}/src/local_knowledge.egg-info/top_level.txt +0 -0
- {local_knowledge-0.2.1 → local_knowledge-0.2.3}/tests/test_agent_doc_consistency.py +0 -0
- {local_knowledge-0.2.1 → local_knowledge-0.2.3}/tests/test_crawl_local.py +0 -0
- {local_knowledge-0.2.1 → local_knowledge-0.2.3}/tests/test_extract_files.py +0 -0
- {local_knowledge-0.2.1 → local_knowledge-0.2.3}/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
|
+
Version: 0.2.3
|
|
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.
|
|
23
|
+
Requires-Dist: confluence-knowledge[models]>=0.5.3; 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.
|
|
7
|
+
version = "0.2.3"
|
|
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.
|
|
30
|
+
"confluence-knowledge[models]>=0.5.3",
|
|
31
31
|
]
|
|
32
32
|
dev = [
|
|
33
33
|
"pytest>=8.0",
|
|
@@ -79,6 +79,9 @@ def main() -> None:
|
|
|
79
79
|
import subprocess
|
|
80
80
|
cmd = [sys.executable, "-m", "pip", "install", "-U",
|
|
81
81
|
"local-knowledge[models]"]
|
|
82
|
+
# a running MCP server holds open files — quit the IDE first
|
|
83
|
+
print("note: quit the IDE (or stop the MCP server) before updating —"
|
|
84
|
+
" a running server can't be replaced cleanly", flush=True)
|
|
82
85
|
if os.name == "nt":
|
|
83
86
|
# this exe is the running process on Windows — pip can't replace
|
|
84
87
|
# a file that's in use; hand the user the equivalent command
|
|
@@ -32,7 +32,7 @@ def resolve_root(path: str) -> Path:
|
|
|
32
32
|
return p.resolve()
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
from confluence_knowledge.retrieval import
|
|
35
|
+
from confluence_knowledge.retrieval import MODEL_MARKER as _MODEL_ID
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
class LocalCrawler:
|
|
@@ -133,6 +133,19 @@ class LocalCrawler:
|
|
|
133
133
|
|
|
134
134
|
queue: list[str] = job["queue"]
|
|
135
135
|
errors: list[str] = job["errors"]
|
|
136
|
+
keep_failed: set[str] = set() # transient read fails keep old content
|
|
137
|
+
# Mid-crawl model change: already-embedded pages live in the OLD
|
|
138
|
+
# vector space — finishing would publish mixed dims. Wipe + fail;
|
|
139
|
+
# start_crawl rebuilds under the new model.
|
|
140
|
+
stamped = self.index.model_id()
|
|
141
|
+
if stamped is not None and stamped != _MODEL_ID and queue:
|
|
142
|
+
self.index.clear_scope()
|
|
143
|
+
errors = errors + [
|
|
144
|
+
(f"model changed mid-crawl ({stamped} → {_MODEL_ID}) — "
|
|
145
|
+
"index cleared; run start_crawl")]
|
|
146
|
+
self.index.save_job(job_id, "error", [], errors)
|
|
147
|
+
return {"job_id": job_id, "state": "error",
|
|
148
|
+
"remaining": 0, "errors": errors}
|
|
136
149
|
seen_raw = self.index.meta_get(f"seen:{job_id}")
|
|
137
150
|
seen: set[str] = set(json.loads(seen_raw)) if seen_raw else set()
|
|
138
151
|
levels_raw = self.index.meta_get(f"levels:{job_id}")
|
|
@@ -174,6 +187,7 @@ class LocalCrawler:
|
|
|
174
187
|
page, chunks = self._page(path)
|
|
175
188
|
except SetupError as e:
|
|
176
189
|
errors.append(str(e))
|
|
190
|
+
keep_failed.add(self._rel(path))
|
|
177
191
|
continue
|
|
178
192
|
rel = page["page_id"]
|
|
179
193
|
if rel in seen:
|
|
@@ -196,7 +210,7 @@ class LocalCrawler:
|
|
|
196
210
|
# persist final seen/levels/warns too — the in-loop checkpoint runs
|
|
197
211
|
# before each item is processed, so the last item would undercount
|
|
198
212
|
self._checkpoint(job_id, queue, errors, seen, levels, warns)
|
|
199
|
-
self.index.publish(seen)
|
|
213
|
+
self.index.publish(seen | keep_failed)
|
|
200
214
|
self.index.set_model_id(_MODEL_ID)
|
|
201
215
|
self.index.save_job(job_id, "done" if not queue else "running",
|
|
202
216
|
queue, errors)
|
|
@@ -14,7 +14,9 @@ import threading
|
|
|
14
14
|
from confluence_knowledge.client import AccessError, SecurityException
|
|
15
15
|
from confluence_knowledge.index import Index
|
|
16
16
|
from confluence_knowledge.retrieval import (
|
|
17
|
-
EMBED_MODEL_ID,
|
|
17
|
+
MODEL_MARKER as EMBED_MODEL_ID,
|
|
18
|
+
)
|
|
19
|
+
from confluence_knowledge.retrieval import (
|
|
18
20
|
LocalEmbedder,
|
|
19
21
|
LocalReranker,
|
|
20
22
|
LocalSparseEmbedder,
|
|
@@ -30,7 +32,7 @@ from .settings_local import APP_NAME, LocalProfile, home, index_root, workdir
|
|
|
30
32
|
mcp = FastMCP("local-knowledge")
|
|
31
33
|
|
|
32
34
|
_scopes: dict[str, dict] = {}
|
|
33
|
-
_evidence_sets: dict[str,
|
|
35
|
+
_evidence_sets: dict[str, dict] = {} # set_id → {scope, ev}
|
|
34
36
|
_indexes: dict[str, Index] = {}
|
|
35
37
|
_EMBEDDER = None
|
|
36
38
|
_RERANKER = None
|
|
@@ -267,7 +269,10 @@ def search(scope_handle: str, question: str) -> dict:
|
|
|
267
269
|
"— run start_crawl once to rebuild it, then search again"}
|
|
268
270
|
ev = retrieve(idx, question, _embedder(), _reranker(), sparse=_sparse())
|
|
269
271
|
set_id = f"ev:{len(_evidence_sets)}:{s['key'][:8]}"
|
|
270
|
-
|
|
272
|
+
# bound the session cache — sets are never reused after the turn ends
|
|
273
|
+
while len(_evidence_sets) >= 32:
|
|
274
|
+
_evidence_sets.pop(next(iter(_evidence_sets)))
|
|
275
|
+
_evidence_sets[set_id] = {"scope": s["key"], "ev": ev}
|
|
271
276
|
return {
|
|
272
277
|
"evidence_set": set_id,
|
|
273
278
|
"coverage": _coverage(idx),
|
|
@@ -275,7 +280,9 @@ def search(scope_handle: str, question: str) -> dict:
|
|
|
275
280
|
"evidence": [
|
|
276
281
|
{"chunk_id": e["chunk_id"], "page_title": e["page_title"],
|
|
277
282
|
"page_url": e["page_url"], "heading": e["heading"],
|
|
278
|
-
"score": e.get("rerank_score"), "text": e["text"][:
|
|
283
|
+
"score": e.get("rerank_score"), "text": e["text"][:12000],
|
|
284
|
+
"low_confidence": e.get("low_confidence", False),
|
|
285
|
+
"truncated": len(e["text"]) > 12000}
|
|
279
286
|
for e in ev
|
|
280
287
|
],
|
|
281
288
|
}
|
|
@@ -301,12 +308,12 @@ def get_evidence(scope_handle: str, chunk_ids: list[str]) -> dict:
|
|
|
301
308
|
@mcp.tool()
|
|
302
309
|
def verify_citations(scope_handle: str, evidence_set: str,
|
|
303
310
|
mappings: list[dict]) -> dict:
|
|
304
|
-
_scope(scope_handle)
|
|
305
|
-
|
|
306
|
-
if
|
|
311
|
+
s = _scope(scope_handle)
|
|
312
|
+
rec = _evidence_sets.get(evidence_set)
|
|
313
|
+
if rec is None or rec["scope"] != s["key"]:
|
|
307
314
|
raise SecurityException(
|
|
308
315
|
"unknown or cross-scope evidence set — run search again and cite its evidence_set")
|
|
309
|
-
return check_citations(ev, mappings)
|
|
316
|
+
return check_citations(rec["ev"], mappings)
|
|
310
317
|
|
|
311
318
|
|
|
312
319
|
def main() -> None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: local-knowledge
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
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.
|
|
23
|
+
Requires-Dist: confluence-knowledge[models]>=0.5.3; 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"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{local_knowledge-0.2.1 → local_knowledge-0.2.3}/src/local_knowledge.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{local_knowledge-0.2.1 → local_knowledge-0.2.3}/src/local_knowledge.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|