ltcai 11.5.0 → 11.5.2
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 +86 -47
- package/docs/CHANGELOG.md +76 -0
- package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
- package/docs/DEVELOPMENT.md +1 -1
- package/docs/HYBRID_CLOUD_KG_STREAMING.md +8 -4
- package/docs/MULTI_AGENT_RUNTIME.md +1 -1
- package/docs/ONBOARDING.md +1 -1
- package/docs/OPERATIONS.md +1 -1
- package/docs/TRUST_MODEL.md +1 -1
- package/docs/WHY_LATTICE.md +1 -1
- package/docs/WORKFLOW_DESIGNER.md +1 -1
- package/docs/architecture.md +4 -4
- package/docs/kg-schema.md +1 -1
- package/docs/v11.5.1_RUST_FULL_LOOP_PLAN.md +85 -0
- package/docs/v11.5.2_TIGHT_SHIP_PLAN.md +144 -0
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/graph/proactive.py +1 -1
- package/lattice_brain/graph/projection/v2_schema.py +0 -28
- package/lattice_brain/graph/vector_index/base.py +0 -3
- package/lattice_brain/graph/vector_index/brute_force.py +0 -4
- package/lattice_brain/graph/vector_index/hnsw.py +0 -3
- package/lattice_brain/graph/vector_index/quantized.py +0 -3
- package/lattice_brain/ingestion_jobs.py +0 -3
- package/lattice_brain/memory.py +0 -27
- package/lattice_brain/portability/sharing.py +0 -4
- package/lattice_brain/quality.py +0 -49
- package/lattice_brain/runtime/agent_runtime.py +1 -1
- package/lattice_brain/runtime/hooks.py +0 -13
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/lattice_brain/sealed_box.py +0 -4
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/admin.py +12 -12
- package/latticeai/api/agent_worker_seam.py +389 -0
- package/latticeai/api/auth.py +25 -2
- package/latticeai/api/chat.py +3 -3
- package/latticeai/api/chat_agent_http.py +3 -12
- package/latticeai/api/chat_helpers.py +10 -13
- package/latticeai/api/computer_use.py +31 -42
- package/latticeai/api/health.py +21 -1
- package/latticeai/api/local_files.py +14 -0
- package/latticeai/api/permissions.py +38 -12
- package/latticeai/api/search.py +24 -0
- package/latticeai/api/static_routes.py +4 -1
- package/latticeai/cli/runtime.py +7 -3
- package/latticeai/core/config.py +47 -3
- package/latticeai/core/csrf.py +28 -2
- package/latticeai/core/embedding_providers/__init__.py +3 -5
- package/latticeai/core/embedding_providers/base.py +1 -1
- package/latticeai/core/embedding_providers/text.py +1 -1
- package/latticeai/core/enterprise.py +1 -4
- package/latticeai/core/http_origin.py +146 -0
- package/latticeai/core/invitations.py +3 -2
- package/latticeai/core/io_utils.py +2 -11
- package/latticeai/core/legacy_compatibility.py +1 -1
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/messages.py +40 -0
- package/latticeai/core/model_compat.py +2 -8
- package/latticeai/core/module_probe.py +37 -0
- package/latticeai/core/run_explain.py +5 -2
- package/latticeai/core/run_store.py +2 -2
- package/latticeai/core/security.py +13 -0
- package/latticeai/core/sessions.py +3 -2
- package/latticeai/core/sse.py +25 -0
- package/latticeai/core/users.py +0 -9
- package/latticeai/core/workspace_graph_trace.py +2 -8
- package/latticeai/core/workspace_os.py +0 -10
- package/latticeai/core/workspace_os_constants.py +1 -1
- package/latticeai/core/workspace_os_utils.py +34 -0
- package/latticeai/core/workspace_review_items.py +2 -8
- package/latticeai/core/workspace_runs.py +2 -8
- package/latticeai/core/workspace_skills.py +2 -7
- package/latticeai/models/router/documents.py +0 -35
- package/latticeai/models/router/registry.py +0 -4
- package/latticeai/runtime/access_runtime.py +16 -4
- package/latticeai/runtime/build_phases/features.py +25 -2
- package/latticeai/runtime/build_phases/web.py +2 -0
- package/latticeai/runtime/feature_toggle_wiring.py +12 -18
- package/latticeai/runtime/network_boundary_wiring.py +8 -19
- package/latticeai/runtime/permission_mode_wiring.py +6 -13
- package/latticeai/runtime/router_registration.py +4 -0
- package/latticeai/runtime/runtime_context.py +1 -13
- package/latticeai/runtime/service_singletons.py +55 -0
- package/latticeai/services/architecture_readiness.py +1 -1
- package/latticeai/services/brain_intelligence/proposals.py +0 -5
- package/latticeai/services/change_proposals.py +2 -36
- package/latticeai/services/chronicle.py +4 -6
- package/latticeai/services/command_center.py +4 -4
- package/latticeai/services/evidence_actions.py +5 -2
- package/latticeai/services/hybrid_chat.py +2 -2
- package/latticeai/services/hybrid_policy.py +8 -57
- package/latticeai/services/mode_store.py +132 -0
- package/latticeai/services/model_capability_registry.py +0 -15
- package/latticeai/services/model_catalog.py +0 -7
- package/latticeai/services/model_loading.py +3 -2
- package/latticeai/services/model_runtime/__init__.py +0 -57
- package/latticeai/services/model_runtime/engines.py +27 -128
- package/latticeai/services/model_runtime/loading.py +2 -2
- package/latticeai/services/network_boundary_service.py +7 -44
- package/latticeai/services/permission_mode_service.py +8 -56
- package/latticeai/services/product_readiness.py +1 -1
- package/latticeai/services/setup_detection.py +67 -1
- package/latticeai/services/tool_dispatch.py +0 -4
- package/latticeai/services/upload_service.py +7 -20
- package/latticeai/services/workspace_service.py +0 -6
- package/latticeai/setup/auto_setup.py +15 -26
- package/latticeai/setup/wizard/catalog.py +5 -2
- package/latticeai/setup/wizard/detect.py +6 -27
- package/latticeai/setup/wizard/paths.py +2 -5
- package/latticeai/setup/wizard/plans.py +2 -2
- package/package.json +2 -4
- package/scripts/bump_version.py +14 -1
- package/scripts/check_current_release_docs.mjs +1 -1
- package/scripts/check_legacy_debt.mjs +1 -1
- package/scripts/check_server_i18n.mjs +1 -0
- package/scripts/generate_agent_loop_fixtures.py +994 -0
- package/scripts/generate_rust_parity_fixtures.py +72 -161
- package/scripts/parity_fixture_corpus_context.py +162 -0
- package/scripts/parity_fixture_corpus_docgen.py +341 -0
- package/scripts/release_screen_claims.json +20 -0
- package/src-tauri/Cargo.lock +13 -7
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/src/backend.rs +76 -2
- package/src-tauri/src/main.rs +13 -3
- package/src-tauri/tauri.conf.json +2 -2
- package/static/app/asset-manifest.json +41 -41
- package/static/app/assets/{Act-CWnxSCgN.js → Act-DcQizkl1.js} +1 -1
- package/static/app/assets/{AdminConsole-BEQYU6kF.js → AdminConsole-cf4npybT.js} +1 -1
- package/static/app/assets/{Brain-DWu1BhFg.js → Brain-3VCSHFcn.js} +1 -1
- package/static/app/assets/{BrainHome-95Hilr9R.js → BrainHome-Qm8eaztx.js} +1 -1
- package/static/app/assets/{BrainSignals-QdeqCpAF.js → BrainSignals-DS9BtKOW.js} +1 -1
- package/static/app/assets/{Capture-BHpCxnzb.js → Capture-DiQ219jW.js} +1 -1
- package/static/app/assets/{Chronicle-B4xYKoed.js → Chronicle-BGvuAchH.js} +1 -1
- package/static/app/assets/{CommandPalette-BVXnttSz.js → CommandPalette-Bqhm0Urn.js} +1 -1
- package/static/app/assets/{Library-DgYcHome.js → Library-BV6NnF0a.js} +1 -1
- package/static/app/assets/{LivingBrain-CrJLDbf7.js → LivingBrain-GzenJchP.js} +1 -1
- package/static/app/assets/{ProductFlow-DFlScKoJ.js → ProductFlow-DEP6-vML.js} +1 -1
- package/static/app/assets/{ReviewCard-Cy5f48Pj.js → ReviewCard-CNZ7XjWG.js} +1 -1
- package/static/app/assets/{System-NF8IfhTa.js → System-CieofHQa.js} +1 -1
- package/static/app/assets/arrow-left-kfsrk0mv.js +1 -0
- package/static/app/assets/{bot-CucuhLhm.js → bot-B_K1Tdmw.js} +1 -1
- package/static/app/assets/{brain-BBnSryW_.js → brain-DWyaV1L1.js} +1 -1
- package/static/app/assets/{button-C2GUj2Ai.js → button-aTn4s84A.js} +1 -1
- package/static/app/assets/circle-check-qqLug9nU.js +1 -0
- package/static/app/assets/{circle-pause-CbkWzBmG.js → circle-pause-xKgeGXkT.js} +1 -1
- package/static/app/assets/{circle-play-7lEaqHdJ.js → circle-play-DkT6tYPX.js} +1 -1
- package/static/app/assets/{cpu-DAlCXlIy.js → cpu-85xYObUC.js} +1 -1
- package/static/app/assets/{download-RNhuuJwh.js → download-B5Fm7YXo.js} +1 -1
- package/static/app/assets/{folder-open-CLW4odzM.js → folder-open-kk2Xa52u.js} +1 -1
- package/static/app/assets/{hard-drive-NKEiDIAJ.js → hard-drive-DkA3zBW_.js} +1 -1
- package/static/app/assets/{index-DMurvUuR.js → index-BMPdTmlY.js} +3 -3
- package/static/app/assets/index-DxmOfNRi.css +2 -0
- package/static/app/assets/{input-D2UhPC1X.js → input-B0nRf2jO.js} +1 -1
- package/static/app/assets/{link-2-6amKbP_P.js → link-2-Dwb4gnTc.js} +1 -1
- package/static/app/assets/{permissionCopy-Cu9TZtdR.js → permissionCopy-CQDUBrOZ.js} +1 -1
- package/static/app/assets/{primitives-gPsccucr.js → primitives-SNp0LRJz.js} +1 -1
- package/static/app/assets/search-BcHqkjoy.js +1 -0
- package/static/app/assets/{share-2-Bau7KkPq.js → share-2-BsrxFglO.js} +1 -1
- package/static/app/assets/{shield-alert-BufNYypi.js → shield-alert-5BStfp2_.js} +1 -1
- package/static/app/assets/{textarea-BQnVWhYs.js → textarea-Cg8IUA-k.js} +1 -1
- package/static/app/assets/{useFocusTrap-B3_w60si.js → useFocusTrap-CYKvE46M.js} +1 -1
- package/static/app/assets/{useMutation-BHhCflT6.js → useMutation-CSn9t1op.js} +1 -1
- package/static/app/assets/{useQuery-rBWfI-5t.js → useQuery-CY2OI2uy.js} +1 -1
- package/static/app/assets/{utils-V_5-wxr5.js → utils-Ddol2RWD.js} +1 -1
- package/static/app/assets/{workspace-K1zjYUHj.js → workspace-BqDwOz_p.js} +1 -1
- package/static/app/index.html +4 -4
- package/static/sw.js +1 -1
- package/desktop/electron/README.md +0 -9
- package/desktop/electron/main.cjs +0 -58
- package/desktop/electron/preload.cjs +0 -5
- package/latticeai/core/graph_curator.py +0 -11
- package/latticeai/core/hooks.py +0 -11
- package/latticeai/core/local_embeddings.py +0 -104
- package/latticeai/core/multi_agent.py +0 -11
- package/latticeai/core/workflow_engine.py +0 -11
- package/latticeai/services/ingestion.py +0 -11
- package/latticeai/services/kg_portability.py +0 -11
- package/latticeai/services/multimodal_streaming.py +0 -129
- package/scripts/measure_brain_home_fill.mjs +0 -141
- package/static/app/assets/arrow-left-DwkSYrjR.js +0 -1
- package/static/app/assets/circle-check-CxOVPwYq.js +0 -1
- package/static/app/assets/index-BLPb5lmE.css +0 -2
- package/static/app/assets/search-Cj_TKk_2.js +0 -1
|
@@ -644,7 +644,7 @@ def gate_ingest_candidate(
|
|
|
644
644
|
"""Score whether new content should be ingested into the graph.
|
|
645
645
|
|
|
646
646
|
Pure function — no store access, no side effects. Intended adoption point
|
|
647
|
-
for the ingestion pipeline (``
|
|
647
|
+
for the ingestion pipeline (``lattice_brain.ingestion`` /
|
|
648
648
|
``lattice_brain.graph.ingest``), which passes a closure over its own
|
|
649
649
|
scoped search, e.g.::
|
|
650
650
|
|
|
@@ -516,31 +516,3 @@ class KnowledgeGraphV2SchemaMixin(_Core):
|
|
|
516
516
|
except Exception as ex:
|
|
517
517
|
logging.debug("knowledge_graph: v2 edge delete mirror skipped: %s", ex)
|
|
518
518
|
|
|
519
|
-
def _v2_sync_report(self) -> Dict[str, Any]:
|
|
520
|
-
"""Diagnose the dual-write invariant: legacy node/edge id sets must equal
|
|
521
|
-
the v2 projection's. Returns counts + any drift (ids missing from / extra
|
|
522
|
-
in v2). ``in_sync`` is True only when both id sets match exactly.
|
|
523
|
-
|
|
524
|
-
All legacy writes go through _upsert_node/_upsert_edge (which dual-write)
|
|
525
|
-
and every legacy delete is mirrored, so a non-empty drift signals a
|
|
526
|
-
bypassed write path — this is the runtime guard for that invariant.
|
|
527
|
-
"""
|
|
528
|
-
if KGStoreV2 is None:
|
|
529
|
-
return {"available": False, "in_sync": True}
|
|
530
|
-
with self._connect() as conn:
|
|
531
|
-
legacy_nodes = {r[0] for r in conn.execute("SELECT id FROM nodes")}
|
|
532
|
-
v2_nodes = {r[0] for r in conn.execute("SELECT id FROM nodes_v2")}
|
|
533
|
-
legacy_edges = {r[0] for r in conn.execute("SELECT id FROM edges")}
|
|
534
|
-
v2_edges = {r[0] for r in conn.execute("SELECT id FROM edges_v2")}
|
|
535
|
-
return {
|
|
536
|
-
"available": True,
|
|
537
|
-
"in_sync": legacy_nodes == v2_nodes and legacy_edges == v2_edges,
|
|
538
|
-
"nodes_legacy": len(legacy_nodes),
|
|
539
|
-
"nodes_v2": len(v2_nodes),
|
|
540
|
-
"edges_legacy": len(legacy_edges),
|
|
541
|
-
"edges_v2": len(v2_edges),
|
|
542
|
-
"nodes_missing_from_v2": sorted(legacy_nodes - v2_nodes),
|
|
543
|
-
"nodes_extra_in_v2": sorted(v2_nodes - legacy_nodes),
|
|
544
|
-
"edges_missing_from_v2": sorted(legacy_edges - v2_edges),
|
|
545
|
-
"edges_extra_in_v2": sorted(v2_edges - legacy_edges),
|
|
546
|
-
}
|
|
@@ -142,9 +142,6 @@ class VectorIndex(Protocol):
|
|
|
142
142
|
def rebuild(self, items: Iterable[IndexItem]) -> None:
|
|
143
143
|
raise NotImplementedError
|
|
144
144
|
|
|
145
|
-
def metadata_for(self, id: str) -> Dict[str, Any]:
|
|
146
|
-
raise NotImplementedError
|
|
147
|
-
|
|
148
145
|
def search(
|
|
149
146
|
self,
|
|
150
147
|
query: Sequence[float],
|
|
@@ -80,10 +80,6 @@ class BruteForceIndex:
|
|
|
80
80
|
for item_id, vector, metadata in items:
|
|
81
81
|
self.add(item_id, vector, metadata)
|
|
82
82
|
|
|
83
|
-
def metadata_for(self, id: str) -> Dict[str, Any]:
|
|
84
|
-
"""Metadata stored alongside ``id`` (empty dict when unknown)."""
|
|
85
|
-
return dict(self._metadata.get(str(id), {}))
|
|
86
|
-
|
|
87
83
|
def search(
|
|
88
84
|
self,
|
|
89
85
|
query: Sequence[float],
|
|
@@ -150,9 +150,6 @@ class HnswIndex:
|
|
|
150
150
|
self.add(item_id, vector, metadata)
|
|
151
151
|
self._dirty = True
|
|
152
152
|
|
|
153
|
-
def metadata_for(self, id: str) -> Dict[str, Any]:
|
|
154
|
-
return dict(self._metadata.get(str(id), {}))
|
|
155
|
-
|
|
156
153
|
# ── graph construction ───────────────────────────────────────────────────
|
|
157
154
|
def _new_graph(self, module: Any, capacity: int) -> Any:
|
|
158
155
|
graph = module.Index(space=self._space, dim=self._dim)
|
|
@@ -109,9 +109,6 @@ class QuantizedIndex:
|
|
|
109
109
|
for item_id, vector, metadata in items:
|
|
110
110
|
self.add(item_id, vector, metadata)
|
|
111
111
|
|
|
112
|
-
def metadata_for(self, id: str) -> Dict[str, Any]:
|
|
113
|
-
return dict(self._metadata.get(str(id), {}))
|
|
114
|
-
|
|
115
112
|
def search(
|
|
116
113
|
self,
|
|
117
114
|
query: Sequence[float],
|
|
@@ -351,9 +351,6 @@ class BackgroundIngestionQueue:
|
|
|
351
351
|
def get(self, job_id: str) -> Optional[BackgroundIngestionJob]:
|
|
352
352
|
return self._jobs.get(job_id)
|
|
353
353
|
|
|
354
|
-
def list_pending(self) -> List[BackgroundIngestionJob]:
|
|
355
|
-
return [j for j in self._jobs.values() if j.status == "queued"]
|
|
356
|
-
|
|
357
354
|
def list_recent(self, limit: int = 20) -> List[BackgroundIngestionJob]:
|
|
358
355
|
"""Most recent jobs first (insertion order is schedule order)."""
|
|
359
356
|
limit = max(1, int(limit))
|
package/lattice_brain/memory.py
CHANGED
|
@@ -27,33 +27,6 @@ class BrainMemory:
|
|
|
27
27
|
def available(self) -> bool:
|
|
28
28
|
return self._pipeline is not None and self._pipeline.available()
|
|
29
29
|
|
|
30
|
-
def record_decision(
|
|
31
|
-
self,
|
|
32
|
-
title: str,
|
|
33
|
-
detail: str = "",
|
|
34
|
-
*,
|
|
35
|
-
user_email: Optional[str] = None,
|
|
36
|
-
workspace_id: Optional[str] = None,
|
|
37
|
-
conversation_id: Optional[str] = None,
|
|
38
|
-
decided_by: Optional[str] = None,
|
|
39
|
-
metadata: Optional[Dict[str, Any]] = None,
|
|
40
|
-
) -> Dict[str, Any]:
|
|
41
|
-
if not str(title or "").strip():
|
|
42
|
-
raise ValueError("a decision needs a title")
|
|
43
|
-
result = self._pipeline.ingest(
|
|
44
|
-
IngestionItem(
|
|
45
|
-
source_type="decision",
|
|
46
|
-
title=title.strip(),
|
|
47
|
-
text=detail,
|
|
48
|
-
owner=user_email,
|
|
49
|
-
workspace_id=workspace_id,
|
|
50
|
-
conversation_id=conversation_id,
|
|
51
|
-
metadata={"decided_by": decided_by or user_email, **(metadata or {})},
|
|
52
|
-
),
|
|
53
|
-
user_email=user_email,
|
|
54
|
-
)
|
|
55
|
-
return result.as_dict()
|
|
56
|
-
|
|
57
30
|
def record_experience(
|
|
58
31
|
self,
|
|
59
32
|
title: str,
|
|
@@ -167,10 +167,6 @@ class KGPortabilitySharingMixin(_Core):
|
|
|
167
167
|
quiet()
|
|
168
168
|
return result
|
|
169
169
|
|
|
170
|
-
def import_from_file(self, path, *, mode: str = "merge", dry_run: bool = False) -> Dict[str, Any]:
|
|
171
|
-
artifact = json.loads(Path(path).read_text(encoding="utf-8"))
|
|
172
|
-
return self.import_data(artifact, mode=mode, dry_run=dry_run)
|
|
173
|
-
|
|
174
170
|
# ── selective subgraph share (opt-in prototype, off by default) ──────────
|
|
175
171
|
def share_status(self) -> Dict[str, Any]:
|
|
176
172
|
"""Honest read of the share surface. Safe to call while it is off.
|
package/lattice_brain/quality.py
CHANGED
|
@@ -375,15 +375,6 @@ class GraphEdgeQualityManager:
|
|
|
375
375
|
seen[key] = e.get("id")
|
|
376
376
|
return dups
|
|
377
377
|
|
|
378
|
-
def merge_duplicate_edges(self, edges: List[Dict]) -> List[Dict]:
|
|
379
|
-
# keep highest confidence
|
|
380
|
-
best: Dict[Any, Dict[str, Any]] = {}
|
|
381
|
-
for e in edges:
|
|
382
|
-
key = (e.get("source"), e.get("target"), e.get("type"))
|
|
383
|
-
if key not in best or e.get("confidence", 0) > best[key].get("confidence", 0):
|
|
384
|
-
best[key] = e
|
|
385
|
-
return list(best.values())
|
|
386
|
-
|
|
387
378
|
def compute_quality_metrics(self, edges: List[Dict]) -> Dict[str, float]:
|
|
388
379
|
if not edges:
|
|
389
380
|
return {"avg_conf": 0.0, "avg_evidence": 0.0, "dup_rate": 0.0}
|
|
@@ -532,46 +523,6 @@ class LatticeBrainQuality:
|
|
|
532
523
|
self.context_assembler = StructuredContextAssembler()
|
|
533
524
|
self.benchmark = RetrievalBenchmarkRunner()
|
|
534
525
|
|
|
535
|
-
def full_quality_pass(self, payload: Dict[str, Any]) -> Dict[str, Any]:
|
|
536
|
-
"""End-to-end quality pipeline (non-destructive)"""
|
|
537
|
-
result = {"status": "ok", "timestamp": time.time()}
|
|
538
|
-
|
|
539
|
-
# embedding
|
|
540
|
-
if "embeddings" in payload:
|
|
541
|
-
labels = [self.embed_labeller.label(e["id"], e.get("vector")) for e in payload["embeddings"]]
|
|
542
|
-
result["embedding_labels"] = [label.__dict__ for label in labels]
|
|
543
|
-
result["reindex_plan"] = self.embed_labeller.generate_reindex_plan()
|
|
544
|
-
|
|
545
|
-
# hybrid retrieval
|
|
546
|
-
if "retrieval" in payload:
|
|
547
|
-
fused = self.hybrid.fuse(payload["retrieval"]["query"], payload["retrieval"]["candidates"])
|
|
548
|
-
reranked = self.reranker.rerank(payload["retrieval"]["query"], fused)
|
|
549
|
-
result["retrieval"] = {"fused": fused, "reranked": reranked}
|
|
550
|
-
|
|
551
|
-
# memory
|
|
552
|
-
if "memories" in payload:
|
|
553
|
-
cands = self.memory_mgr.extract_candidates(payload["memories"])
|
|
554
|
-
cands = self.memory_mgr.score_candidates(cands, payload.get("query", ""))
|
|
555
|
-
cands = self.memory_mgr.dedupe(cands)
|
|
556
|
-
cands = self.memory_mgr.merge(cands)
|
|
557
|
-
cands = self.memory_mgr.detect_conflicts(cands)
|
|
558
|
-
cands = self.memory_mgr.apply_retention(cands)
|
|
559
|
-
result["memory_candidates"] = [c.__dict__ for c in cands]
|
|
560
|
-
|
|
561
|
-
# graph
|
|
562
|
-
if "graph_edges" in payload:
|
|
563
|
-
eqs = [self.graph_mgr.validate_edge(e) for e in payload["graph_edges"]]
|
|
564
|
-
result["graph_quality"] = [q.__dict__ for q in eqs]
|
|
565
|
-
result["graph_metrics"] = self.graph_mgr.compute_quality_metrics(payload["graph_edges"])
|
|
566
|
-
|
|
567
|
-
# context
|
|
568
|
-
if "context_items" in payload:
|
|
569
|
-
ctx = self.context_assembler.assemble(payload["context_items"])
|
|
570
|
-
ctx = self.context_assembler.apply_guardrails(ctx)
|
|
571
|
-
result["structured_context"] = ctx
|
|
572
|
-
|
|
573
|
-
return result
|
|
574
|
-
|
|
575
526
|
|
|
576
527
|
__all__ = [
|
|
577
528
|
"BM25Scorer",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
진입점: app_factory.py:AGENT_RUNTIME (wiring root), api/agents.py, RunExecutor.
|
|
9
9
|
|
|
10
10
|
Before this module the agent concern was spread across three places: the
|
|
11
|
-
:class:`~
|
|
11
|
+
:class:`~lattice_brain.runtime.multi_agent.MultiAgentOrchestrator` (role pipeline),
|
|
12
12
|
the :class:`~latticeai.services.platform_runtime.PlatformRuntime` (cross-system
|
|
13
13
|
wiring + an ad-hoc ``run_agent``), and ``api/agents.py`` (HTTP transport that
|
|
14
14
|
also owned orchestration + persistence + audit). The frontend reached past all
|
|
@@ -181,16 +181,6 @@ class HookResult:
|
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
|
|
184
|
-
def hook_context(kind: str, event: str = "", **kwargs: Any) -> HookContext:
|
|
185
|
-
"""Factory for a :class:`HookContext` (matches the public hook vocabulary)."""
|
|
186
|
-
return HookContext(kind, event, **kwargs)
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
def hook_result(**kwargs: Any) -> HookResult:
|
|
190
|
-
"""Factory for a :class:`HookResult`."""
|
|
191
|
-
return HookResult(**kwargs)
|
|
192
|
-
|
|
193
|
-
|
|
194
184
|
def dispatch_tool(
|
|
195
185
|
hooks: Any,
|
|
196
186
|
tool_name: str,
|
|
@@ -525,9 +515,6 @@ class HooksRegistry:
|
|
|
525
515
|
# Alias — descriptive name used by the wiring layer.
|
|
526
516
|
register_runtime_hook = register_hook
|
|
527
517
|
|
|
528
|
-
def unregister_hook(self, hook_id: str) -> None:
|
|
529
|
-
self._runtime_runners.pop(hook_id, None)
|
|
530
|
-
|
|
531
518
|
def has_runner(self, hook_id: str) -> bool:
|
|
532
519
|
return hook_id in self._runtime_runners
|
|
533
520
|
|
|
@@ -49,7 +49,7 @@ from typing import Any, Callable, Dict, List, Optional
|
|
|
49
49
|
from ..utils import now_iso as _now
|
|
50
50
|
from .contracts import multi_agent_contract
|
|
51
51
|
|
|
52
|
-
MULTI_AGENT_VERSION = "11.5.
|
|
52
|
+
MULTI_AGENT_VERSION = "11.5.2"
|
|
53
53
|
|
|
54
54
|
AGENT_ROLES = ("researcher", "planner", "executor", "reviewer", "release")
|
|
55
55
|
CORE_PIPELINE = ("planner", "executor", "reviewer")
|
|
@@ -212,10 +212,6 @@ class RecipientIdentity:
|
|
|
212
212
|
"path": str(self._key_file),
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
def seal_to_self(self, plaintext: bytes) -> Dict[str, Any]:
|
|
216
|
-
"""Seal to this Brain's own key — the honest way to test a round trip."""
|
|
217
|
-
return seal(plaintext, recipient_public_key=self.public_key_b64)
|
|
218
|
-
|
|
219
215
|
def unseal(self, block: Dict[str, Any]) -> bytes:
|
|
220
216
|
return unseal(block, self._private)
|
|
221
217
|
|
package/latticeai/__init__.py
CHANGED
package/latticeai/api/admin.py
CHANGED
|
@@ -8,6 +8,8 @@ from typing import Any, Callable, Dict, List, Optional
|
|
|
8
8
|
from fastapi import APIRouter, Query, Request
|
|
9
9
|
from pydantic import BaseModel
|
|
10
10
|
|
|
11
|
+
from latticeai.api.workspace_scope import requested_workspace
|
|
12
|
+
from latticeai.core.http_origin import request_external_origin
|
|
11
13
|
from latticeai.core.messages import http_error, resolve_language
|
|
12
14
|
from latticeai.core.workspace_os import DEFAULT_WORKSPACE_ID
|
|
13
15
|
|
|
@@ -65,13 +67,6 @@ def create_admin_router(
|
|
|
65
67
|
) -> APIRouter:
|
|
66
68
|
router = APIRouter()
|
|
67
69
|
|
|
68
|
-
def _workspace_scope(request: Request) -> Optional[str]:
|
|
69
|
-
header = request.headers.get("X-Workspace-Id")
|
|
70
|
-
if header and header.strip():
|
|
71
|
-
return header.strip()
|
|
72
|
-
query = request.query_params.get("workspace_id")
|
|
73
|
-
return query.strip() if query and query.strip() else None
|
|
74
|
-
|
|
75
70
|
def _matches_scope(item: Dict[str, object], workspace_id: Optional[str]) -> bool:
|
|
76
71
|
if not workspace_id:
|
|
77
72
|
return True
|
|
@@ -81,11 +76,11 @@ def create_admin_router(
|
|
|
81
76
|
return str(item_scope or "") == str(workspace_id)
|
|
82
77
|
|
|
83
78
|
def _scoped_history(request: Request) -> List[Dict]:
|
|
84
|
-
scope =
|
|
79
|
+
scope = requested_workspace(request)
|
|
85
80
|
return [item for item in get_history() if _matches_scope(item, scope)]
|
|
86
81
|
|
|
87
82
|
def _scoped_audit_log(request: Request) -> List[Dict]:
|
|
88
|
-
scope =
|
|
83
|
+
scope = requested_workspace(request)
|
|
89
84
|
return [item for item in get_audit_log() if _matches_scope(item, scope)]
|
|
90
85
|
|
|
91
86
|
def _filter_audit_log(
|
|
@@ -427,9 +422,14 @@ def create_admin_router(
|
|
|
427
422
|
@router.get("/admin/invite-link")
|
|
428
423
|
async def admin_invite_link(request: Request):
|
|
429
424
|
require_admin(request)
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
425
|
+
# The link is *mailed to a person*, so it has to name the door they can
|
|
426
|
+
# actually knock on. Behind the desktop gateway ``Host`` is the internal
|
|
427
|
+
# worker authority, which meant every invite emitted through the front
|
|
428
|
+
# door pointed at a port only the gateway can reach.
|
|
429
|
+
origin = request_external_origin(
|
|
430
|
+
request, fallback=f"http://localhost:{default_port}",
|
|
431
|
+
)
|
|
432
|
+
url = f"{origin}/?code={invite_code}" if invite_gate_enabled else f"{origin}/"
|
|
433
433
|
return {"invite_url": url, "invite_code": invite_code, "gate_enabled": invite_gate_enabled}
|
|
434
434
|
|
|
435
435
|
@router.get("/admin/sso")
|