ltcai 6.3.0 → 6.4.0
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.
- package/README.md +38 -36
- package/docs/CHANGELOG.md +55 -0
- package/frontend/src/features/review/ReviewCard.tsx +12 -10
- package/frontend/src/features/review/ReviewInbox.tsx +15 -10
- package/frontend/src/i18n.ts +116 -0
- package/frontend/src/pages/Capture.tsx +48 -41
- package/lattice_brain/__init__.py +3 -1
- package/lattice_brain/quality.py +409 -0
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/knowledge_graph.py +71 -10
- package/latticeai/api/local_files.py +2 -0
- package/latticeai/api/memory.py +6 -6
- package/latticeai/api/search.py +9 -1
- package/latticeai/api/tools.py +3 -0
- package/latticeai/app_factory.py +17 -67
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/runtime/access_runtime.py +97 -0
- package/latticeai/runtime/chat_wiring.py +1 -0
- package/latticeai/services/memory_service.py +64 -21
- package/latticeai/services/router_context.py +4 -0
- package/latticeai/services/search_service.py +61 -10
- package/package.json +1 -1
- package/src-tauri/Cargo.lock +1 -1
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/tauri.conf.json +1 -1
- package/static/app/asset-manifest.json +3 -3
- package/static/app/assets/{index-D76dWuQk.js → index-t1jx1BR9.js} +2 -2
- package/static/app/assets/index-t1jx1BR9.js.map +1 -0
- package/static/app/index.html +1 -1
- package/static/app/assets/index-D76dWuQk.js.map +0 -1
|
@@ -179,10 +179,12 @@ class SearchService:
|
|
|
179
179
|
allowed_workspaces=None,
|
|
180
180
|
) -> Dict[str, Any]:
|
|
181
181
|
weights = {**DEFAULT_HYBRID_WEIGHTS, **dict(weights or {})}
|
|
182
|
+
# Scope each channel at the source so out-of-scope rows never enter the
|
|
183
|
+
# fusion set (defense-in-depth — the fused result is re-scoped below too).
|
|
182
184
|
channels = {
|
|
183
|
-
"keyword": self.keyword_search(query, limit=keyword_limit),
|
|
184
|
-
"vector": self.vector_search(query, limit=vector_limit),
|
|
185
|
-
"graph": self.graph_search(query, limit=graph_limit),
|
|
185
|
+
"keyword": self.keyword_search(query, limit=keyword_limit, allowed_workspaces=allowed_workspaces),
|
|
186
|
+
"vector": self.vector_search(query, limit=vector_limit, allowed_workspaces=allowed_workspaces),
|
|
187
|
+
"graph": self.graph_search(query, limit=graph_limit, allowed_workspaces=allowed_workspaces),
|
|
186
188
|
}
|
|
187
189
|
fused: Dict[str, Dict[str, Any]] = {}
|
|
188
190
|
for source, payload in channels.items():
|
|
@@ -234,14 +236,50 @@ class SearchService:
|
|
|
234
236
|
"matches": matches,
|
|
235
237
|
}
|
|
236
238
|
|
|
237
|
-
def graph(self, *, limit: int = 300) -> Dict[str, Any]:
|
|
238
|
-
return self._require_graph().graph(limit=limit)
|
|
239
|
-
|
|
240
|
-
def node(self, node_id: str, *, include_neighbors: bool = True, depth: int = 1, limit: int = 100) -> Dict[str, Any]:
|
|
239
|
+
def graph(self, *, limit: int = 300, allowed_workspaces=None) -> Dict[str, Any]:
|
|
241
240
|
graph = self._require_graph()
|
|
242
|
-
|
|
241
|
+
try:
|
|
242
|
+
return graph.graph(limit=limit, allowed_workspaces=allowed_workspaces)
|
|
243
|
+
except TypeError:
|
|
244
|
+
payload = graph.graph(limit=limit)
|
|
245
|
+
if allowed_workspaces is not None:
|
|
246
|
+
nodes = graph.filter_scoped_nodes(payload.get("nodes", []), allowed_workspaces)
|
|
247
|
+
kept = {node.get("id") for node in nodes}
|
|
248
|
+
edges = [
|
|
249
|
+
edge for edge in payload.get("edges", [])
|
|
250
|
+
if edge.get("from") in kept and edge.get("to") in kept
|
|
251
|
+
]
|
|
252
|
+
payload = {**payload, "nodes": nodes, "edges": edges}
|
|
253
|
+
return payload
|
|
254
|
+
|
|
255
|
+
def node(
|
|
256
|
+
self,
|
|
257
|
+
node_id: str,
|
|
258
|
+
*,
|
|
259
|
+
include_neighbors: bool = True,
|
|
260
|
+
depth: int = 1,
|
|
261
|
+
limit: int = 100,
|
|
262
|
+
allowed_workspaces=None,
|
|
263
|
+
) -> Dict[str, Any]:
|
|
264
|
+
graph = self._require_graph()
|
|
265
|
+
node = graph.get_node(node_id)
|
|
266
|
+
if allowed_workspaces is not None:
|
|
267
|
+
visible = graph.filter_scoped_nodes([node], allowed_workspaces)
|
|
268
|
+
if not visible:
|
|
269
|
+
raise ValueError(f"graph node not found: {node_id}")
|
|
270
|
+
node = visible[0]
|
|
271
|
+
payload = {"node": node}
|
|
243
272
|
if include_neighbors:
|
|
244
|
-
|
|
273
|
+
neighborhood = graph.traverse(node_id, depth=depth, limit=limit)
|
|
274
|
+
if allowed_workspaces is not None:
|
|
275
|
+
nodes = graph.filter_scoped_nodes(neighborhood.get("nodes", []), allowed_workspaces)
|
|
276
|
+
kept = {item.get("id") for item in nodes}
|
|
277
|
+
edges = [
|
|
278
|
+
edge for edge in neighborhood.get("edges", [])
|
|
279
|
+
if edge.get("from") in kept and edge.get("to") in kept
|
|
280
|
+
]
|
|
281
|
+
neighborhood = {**neighborhood, "nodes": nodes, "edges": edges}
|
|
282
|
+
payload["neighborhood"] = neighborhood
|
|
245
283
|
return payload
|
|
246
284
|
|
|
247
285
|
def relationships(
|
|
@@ -251,13 +289,26 @@ class SearchService:
|
|
|
251
289
|
node_id: str = "",
|
|
252
290
|
relationship_type: str = "",
|
|
253
291
|
limit: int = 30,
|
|
292
|
+
allowed_workspaces=None,
|
|
254
293
|
) -> Dict[str, Any]:
|
|
255
|
-
|
|
294
|
+
graph = self._require_graph()
|
|
295
|
+
payload = graph.relationship_search(
|
|
256
296
|
query=query,
|
|
257
297
|
node_id=node_id,
|
|
258
298
|
relationship_type=relationship_type,
|
|
259
299
|
limit=limit,
|
|
260
300
|
)
|
|
301
|
+
if allowed_workspaces is not None:
|
|
302
|
+
kept = []
|
|
303
|
+
for rel in payload.get("relationships", []):
|
|
304
|
+
endpoints = [
|
|
305
|
+
{"id": (rel.get("source") or {}).get("id")},
|
|
306
|
+
{"id": (rel.get("target") or {}).get("id")},
|
|
307
|
+
]
|
|
308
|
+
if len(graph.filter_scoped_nodes(endpoints, allowed_workspaces)) == 2:
|
|
309
|
+
kept.append(rel)
|
|
310
|
+
payload = {**payload, "relationships": kept}
|
|
311
|
+
return payload
|
|
261
312
|
|
|
262
313
|
def index_status(self) -> Dict[str, Any]:
|
|
263
314
|
return self._require_graph().index_status()
|
package/package.json
CHANGED
package/src-tauri/Cargo.lock
CHANGED
package/src-tauri/Cargo.toml
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "6.
|
|
2
|
+
"version": "6.4.0",
|
|
3
3
|
"generated_at": "vite",
|
|
4
4
|
"entrypoints": {
|
|
5
5
|
"app": "/static/app/index.html"
|
|
6
6
|
},
|
|
7
7
|
"assets": {
|
|
8
8
|
"../node_modules/@tauri-apps/api/core.js": "/static/app/assets/core-CwxXejkd.js",
|
|
9
|
-
"index.html": "/static/app/assets/index-
|
|
9
|
+
"index.html": "/static/app/assets/index-t1jx1BR9.js",
|
|
10
10
|
"assets/index-Div5vMlq.css": "/static/app/assets/index-Div5vMlq.css"
|
|
11
11
|
},
|
|
12
12
|
"vite": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"isDynamicEntry": true
|
|
18
18
|
},
|
|
19
19
|
"index.html": {
|
|
20
|
-
"file": "assets/index-
|
|
20
|
+
"file": "assets/index-t1jx1BR9.js",
|
|
21
21
|
"name": "index",
|
|
22
22
|
"src": "index.html",
|
|
23
23
|
"isEntry": true,
|