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
|
@@ -12,12 +12,9 @@ ack is audited. Hard node filters remain in ``latticeai.core.network_boundary``.
|
|
|
12
12
|
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
15
|
-
import json
|
|
16
|
-
import threading
|
|
17
15
|
from pathlib import Path
|
|
18
16
|
from typing import Any, Callable, Dict, Optional
|
|
19
17
|
|
|
20
|
-
from latticeai.core.io_utils import atomic_write_json
|
|
21
18
|
from latticeai.core.network_boundary import (
|
|
22
19
|
DEFAULT_NETWORK_MODE,
|
|
23
20
|
NetworkBoundaryMode,
|
|
@@ -25,11 +22,14 @@ from latticeai.core.network_boundary import (
|
|
|
25
22
|
network_mode_contract,
|
|
26
23
|
normalize_network_mode,
|
|
27
24
|
)
|
|
25
|
+
from latticeai.services.mode_store import JsonBackedModeService
|
|
28
26
|
|
|
29
27
|
|
|
30
|
-
class NetworkBoundaryService:
|
|
28
|
+
class NetworkBoundaryService(JsonBackedModeService[NetworkBoundaryMode]):
|
|
31
29
|
"""Load / save the network boundary dial."""
|
|
32
30
|
|
|
31
|
+
FILENAME = "network_boundary.json"
|
|
32
|
+
|
|
33
33
|
def __init__(
|
|
34
34
|
self,
|
|
35
35
|
*,
|
|
@@ -37,36 +37,11 @@ class NetworkBoundaryService:
|
|
|
37
37
|
default_mode: NetworkBoundaryMode | str = DEFAULT_NETWORK_MODE,
|
|
38
38
|
audit: Optional[Callable[..., None]] = None,
|
|
39
39
|
) -> None:
|
|
40
|
-
|
|
40
|
+
super().__init__(data_dir=data_dir, audit=audit)
|
|
41
41
|
self._default = normalize_network_mode(default_mode)
|
|
42
|
-
self._audit = audit or (lambda *a, **kw: None)
|
|
43
|
-
self._lock = threading.Lock()
|
|
44
|
-
|
|
45
|
-
def rebind_data_dir(self, data_dir: Path) -> None:
|
|
46
|
-
with self._lock:
|
|
47
|
-
self._path = Path(data_dir) / "network_boundary.json"
|
|
48
42
|
|
|
49
|
-
def
|
|
50
|
-
|
|
51
|
-
self._audit = audit
|
|
52
|
-
|
|
53
|
-
def _read(self) -> Dict[str, Any]:
|
|
54
|
-
if not self._path.exists():
|
|
55
|
-
return {"default": self._default.value, "users": {}, "workspaces": {}}
|
|
56
|
-
try:
|
|
57
|
-
data = json.loads(self._path.read_text(encoding="utf-8"))
|
|
58
|
-
except Exception:
|
|
59
|
-
return {"default": self._default.value, "users": {}, "workspaces": {}}
|
|
60
|
-
if not isinstance(data, dict):
|
|
61
|
-
return {"default": self._default.value, "users": {}, "workspaces": {}}
|
|
62
|
-
data.setdefault("default", self._default.value)
|
|
63
|
-
data.setdefault("users", {})
|
|
64
|
-
data.setdefault("workspaces", {})
|
|
65
|
-
return data
|
|
66
|
-
|
|
67
|
-
def _write(self, data: Dict[str, Any]) -> None:
|
|
68
|
-
self._path.parent.mkdir(parents=True, exist_ok=True)
|
|
69
|
-
atomic_write_json(self._path, data)
|
|
43
|
+
def _default_entry(self) -> Any:
|
|
44
|
+
return self._default.value
|
|
70
45
|
|
|
71
46
|
def _resolve_from(
|
|
72
47
|
self,
|
|
@@ -85,18 +60,6 @@ class NetworkBoundaryService:
|
|
|
85
60
|
return normalize_network_mode(user)
|
|
86
61
|
return normalize_network_mode(data.get("default") or self._default)
|
|
87
62
|
|
|
88
|
-
def resolve(
|
|
89
|
-
self,
|
|
90
|
-
*,
|
|
91
|
-
user_email: Optional[str] = None,
|
|
92
|
-
workspace_id: Optional[str] = None,
|
|
93
|
-
) -> NetworkBoundaryMode:
|
|
94
|
-
with self._lock:
|
|
95
|
-
data = self._read()
|
|
96
|
-
return self._resolve_from(
|
|
97
|
-
data, user_email=user_email, workspace_id=workspace_id,
|
|
98
|
-
)
|
|
99
|
-
|
|
100
63
|
def get(
|
|
101
64
|
self,
|
|
102
65
|
*,
|
|
@@ -14,12 +14,9 @@ not by this store.
|
|
|
14
14
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
|
-
import json
|
|
18
|
-
import threading
|
|
19
17
|
from pathlib import Path
|
|
20
18
|
from typing import Any, Callable, Dict, Optional
|
|
21
19
|
|
|
22
|
-
from latticeai.core.io_utils import atomic_write_json
|
|
23
20
|
from latticeai.core.permission_mode import (
|
|
24
21
|
DEFAULT_MODE,
|
|
25
22
|
PermissionMode,
|
|
@@ -27,11 +24,14 @@ from latticeai.core.permission_mode import (
|
|
|
27
24
|
mode_contract,
|
|
28
25
|
normalize_mode,
|
|
29
26
|
)
|
|
27
|
+
from latticeai.services.mode_store import JsonBackedModeService
|
|
30
28
|
|
|
31
29
|
|
|
32
|
-
class PermissionModeService:
|
|
30
|
+
class PermissionModeService(JsonBackedModeService[PermissionMode]):
|
|
33
31
|
"""Load / save the autonomy dial."""
|
|
34
32
|
|
|
33
|
+
FILENAME = "permission_mode.json"
|
|
34
|
+
|
|
35
35
|
def __init__(
|
|
36
36
|
self,
|
|
37
37
|
*,
|
|
@@ -39,43 +39,11 @@ class PermissionModeService:
|
|
|
39
39
|
default_mode: PermissionMode | str = DEFAULT_MODE,
|
|
40
40
|
audit: Optional[Callable[..., None]] = None,
|
|
41
41
|
) -> None:
|
|
42
|
-
|
|
42
|
+
super().__init__(data_dir=data_dir, audit=audit)
|
|
43
43
|
self._default = normalize_mode(default_mode)
|
|
44
|
-
self._audit = audit or (lambda *a, **kw: None)
|
|
45
|
-
self._lock = threading.Lock()
|
|
46
|
-
|
|
47
|
-
def rebind_data_dir(self, data_dir: Path) -> None:
|
|
48
|
-
"""Point the store at the app's real data dir.
|
|
49
|
-
|
|
50
|
-
The wiring may instantiate this service lazily before routers know the
|
|
51
|
-
configured data dir; rebinding keeps one file of record instead of
|
|
52
|
-
stranding writes under the fallback path.
|
|
53
|
-
"""
|
|
54
|
-
with self._lock:
|
|
55
|
-
self._path = Path(data_dir) / "permission_mode.json"
|
|
56
44
|
|
|
57
|
-
def
|
|
58
|
-
|
|
59
|
-
with self._lock:
|
|
60
|
-
self._audit = audit
|
|
61
|
-
|
|
62
|
-
def _read(self) -> Dict[str, Any]:
|
|
63
|
-
if not self._path.exists():
|
|
64
|
-
return {"default": self._default.value, "users": {}, "workspaces": {}}
|
|
65
|
-
try:
|
|
66
|
-
data = json.loads(self._path.read_text(encoding="utf-8"))
|
|
67
|
-
except Exception:
|
|
68
|
-
return {"default": self._default.value, "users": {}, "workspaces": {}}
|
|
69
|
-
if not isinstance(data, dict):
|
|
70
|
-
return {"default": self._default.value, "users": {}, "workspaces": {}}
|
|
71
|
-
data.setdefault("default", self._default.value)
|
|
72
|
-
data.setdefault("users", {})
|
|
73
|
-
data.setdefault("workspaces", {})
|
|
74
|
-
return data
|
|
75
|
-
|
|
76
|
-
def _write(self, data: Dict[str, Any]) -> None:
|
|
77
|
-
self._path.parent.mkdir(parents=True, exist_ok=True)
|
|
78
|
-
atomic_write_json(self._path, data)
|
|
45
|
+
def _default_entry(self) -> Any:
|
|
46
|
+
return self._default.value
|
|
79
47
|
|
|
80
48
|
def _resolve_from(
|
|
81
49
|
self,
|
|
@@ -84,11 +52,7 @@ class PermissionModeService:
|
|
|
84
52
|
user_email: Optional[str],
|
|
85
53
|
workspace_id: Optional[str],
|
|
86
54
|
) -> PermissionMode:
|
|
87
|
-
"""Scope precedence: workspace → user → process default.
|
|
88
|
-
|
|
89
|
-
Pure over ``data`` and lock-free, so holders of ``_lock`` can reuse it
|
|
90
|
-
without re-entering a non-reentrant lock.
|
|
91
|
-
"""
|
|
55
|
+
"""Scope precedence: workspace → user → process default."""
|
|
92
56
|
if workspace_id:
|
|
93
57
|
ws = (data.get("workspaces") or {}).get(str(workspace_id))
|
|
94
58
|
if ws:
|
|
@@ -99,18 +63,6 @@ class PermissionModeService:
|
|
|
99
63
|
return normalize_mode(user)
|
|
100
64
|
return normalize_mode(data.get("default") or self._default)
|
|
101
65
|
|
|
102
|
-
def resolve(
|
|
103
|
-
self,
|
|
104
|
-
*,
|
|
105
|
-
user_email: Optional[str] = None,
|
|
106
|
-
workspace_id: Optional[str] = None,
|
|
107
|
-
) -> PermissionMode:
|
|
108
|
-
with self._lock:
|
|
109
|
-
data = self._read()
|
|
110
|
-
return self._resolve_from(
|
|
111
|
-
data, user_email=user_email, workspace_id=workspace_id,
|
|
112
|
-
)
|
|
113
|
-
|
|
114
66
|
def get(
|
|
115
67
|
self,
|
|
116
68
|
*,
|
|
@@ -50,6 +50,64 @@ def parse_windows_video_controllers(raw: str) -> List[Dict[str, Any]]:
|
|
|
50
50
|
return controllers
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
|
|
54
|
+
def parse_windows_cpu_info(
|
|
55
|
+
raw: str,
|
|
56
|
+
*,
|
|
57
|
+
model: str,
|
|
58
|
+
physical_cores: int,
|
|
59
|
+
logical_cores: int,
|
|
60
|
+
) -> Tuple[str, int, int]:
|
|
61
|
+
"""Read ``wmic cpu get Name,NumberOfCores,NumberOfLogicalProcessors``.
|
|
62
|
+
|
|
63
|
+
Whatever the output does not say keeps the value the caller already had —
|
|
64
|
+
``wmic`` is absent on modern Windows images and prints nothing there, and
|
|
65
|
+
"0 cores" would be a worse answer than ``os.cpu_count()``'s guess.
|
|
66
|
+
"""
|
|
67
|
+
for line in raw.splitlines():
|
|
68
|
+
key, _, value = line.partition("=")
|
|
69
|
+
if key == "Name" and value.strip():
|
|
70
|
+
model = value.strip()
|
|
71
|
+
elif key == "NumberOfCores" and value.strip():
|
|
72
|
+
try:
|
|
73
|
+
physical_cores = int(value.strip())
|
|
74
|
+
except ValueError:
|
|
75
|
+
quiet()
|
|
76
|
+
elif key == "NumberOfLogicalProcessors" and value.strip():
|
|
77
|
+
try:
|
|
78
|
+
logical_cores = int(value.strip())
|
|
79
|
+
except ValueError:
|
|
80
|
+
quiet()
|
|
81
|
+
return model, physical_cores, logical_cores
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
#: ``IsProcessorFeaturePresent`` codes worth reporting, by instruction name.
|
|
85
|
+
WINDOWS_PROCESSOR_FEATURES: Dict[int, str] = {
|
|
86
|
+
6: "sse", 10: "sse2", 13: "sse3", 19: "neon", 28: "rdrand",
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def windows_processor_features() -> List[str]:
|
|
91
|
+
"""Instruction-set flags from the Win32 API, or none anywhere else.
|
|
92
|
+
|
|
93
|
+
``ctypes.windll`` exists only on Windows, so on every other platform this
|
|
94
|
+
raises and answers "nothing known" — which is the honest reading, since the
|
|
95
|
+
flags are unobtainable rather than absent.
|
|
96
|
+
"""
|
|
97
|
+
try:
|
|
98
|
+
import ctypes
|
|
99
|
+
|
|
100
|
+
kernel32 = ctypes.windll.kernel32 # type: ignore[attr-defined] # Windows-only
|
|
101
|
+
return [
|
|
102
|
+
name
|
|
103
|
+
for code, name in WINDOWS_PROCESSOR_FEATURES.items()
|
|
104
|
+
if kernel32.IsProcessorFeaturePresent(code)
|
|
105
|
+
]
|
|
106
|
+
except Exception:
|
|
107
|
+
quiet()
|
|
108
|
+
return []
|
|
109
|
+
|
|
110
|
+
|
|
53
111
|
def detect_cuda(which: WhichFn, run: RunFn) -> Tuple[bool, str, Optional[str], Optional[str]]:
|
|
54
112
|
nvidia_smi = which("nvidia-smi")
|
|
55
113
|
nvcc = which("nvcc")
|
|
@@ -78,4 +136,12 @@ def detect_tools(which: WhichFn, binaries: Iterable[str]) -> Dict[str, Optional[
|
|
|
78
136
|
return {binary: which(binary) for binary in binaries}
|
|
79
137
|
|
|
80
138
|
|
|
81
|
-
__all__ = [
|
|
139
|
+
__all__ = [
|
|
140
|
+
"WINDOWS_PROCESSOR_FEATURES",
|
|
141
|
+
"detect_cuda",
|
|
142
|
+
"detect_tools",
|
|
143
|
+
"detect_wsl_from_text",
|
|
144
|
+
"parse_windows_cpu_info",
|
|
145
|
+
"parse_windows_video_controllers",
|
|
146
|
+
"windows_processor_features",
|
|
147
|
+
]
|
|
@@ -362,10 +362,6 @@ def configure_tool_dispatch(
|
|
|
362
362
|
)
|
|
363
363
|
|
|
364
364
|
|
|
365
|
-
def agent_policy(action_name: str, args: dict) -> ToolPolicy:
|
|
366
|
-
return DEFAULT_TOOL_DISPATCH_SERVICE.policy_for(action_name, args)
|
|
367
|
-
|
|
368
|
-
|
|
369
365
|
def agent_risk(action_name: str, args: dict) -> str:
|
|
370
366
|
return DEFAULT_TOOL_DISPATCH_SERVICE.risk_level(action_name, args)
|
|
371
367
|
|
|
@@ -6,23 +6,15 @@ import logging
|
|
|
6
6
|
import tempfile
|
|
7
7
|
from datetime import datetime
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
from typing import Optional
|
|
10
9
|
|
|
11
10
|
from fastapi import HTTPException, Request, UploadFile
|
|
12
11
|
|
|
13
12
|
from lattice_brain.ingestion import IngestionItem
|
|
13
|
+
from latticeai.api.workspace_scope import resolve_workspace_scope
|
|
14
14
|
from latticeai.core.quiet import quiet
|
|
15
15
|
from latticeai.tools import ToolError, read_document
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
def _workspace_scope_from_request(request: Request) -> Optional[str]:
|
|
19
|
-
header = request.headers.get("X-Workspace-Id")
|
|
20
|
-
if header and header.strip():
|
|
21
|
-
return header.strip()
|
|
22
|
-
query = request.query_params.get("workspace_id")
|
|
23
|
-
return query.strip() if query and query.strip() else None
|
|
24
|
-
|
|
25
|
-
|
|
26
18
|
async def process_uploaded_document(
|
|
27
19
|
*,
|
|
28
20
|
request: Request,
|
|
@@ -39,17 +31,12 @@ async def process_uploaded_document(
|
|
|
39
31
|
workspace_service=None,
|
|
40
32
|
) -> dict:
|
|
41
33
|
enforce_rate_limit(current_user, "upload")
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
)
|
|
49
|
-
except PermissionError as exc:
|
|
50
|
-
raise HTTPException(status_code=403, detail=str(exc)) from exc
|
|
51
|
-
else:
|
|
52
|
-
workspace_id = requested_workspace
|
|
34
|
+
workspace_id = resolve_workspace_scope(
|
|
35
|
+
request,
|
|
36
|
+
user=current_user,
|
|
37
|
+
workspace_service=workspace_service,
|
|
38
|
+
write=True,
|
|
39
|
+
)
|
|
53
40
|
suffix = Path(file.filename or "upload").suffix.lower()
|
|
54
41
|
allowed = {".pdf", ".docx", ".xlsx", ".pptx", ".txt", ".md", ".csv"}
|
|
55
42
|
if suffix not in allowed:
|
|
@@ -73,12 +73,6 @@ class WorkspaceService:
|
|
|
73
73
|
self._ensure_permission(workspace_id, user_id, "write")
|
|
74
74
|
return workspace_id
|
|
75
75
|
|
|
76
|
-
def can_read(self, workspace_id: str, user_id: Optional[str]) -> bool:
|
|
77
|
-
return self.store.has_permission(workspace_id, self._identity(user_id), "read")
|
|
78
|
-
|
|
79
|
-
def can_write(self, workspace_id: str, user_id: Optional[str]) -> bool:
|
|
80
|
-
return self.store.has_permission(workspace_id, self._identity(user_id), "write")
|
|
81
|
-
|
|
82
76
|
def readable_workspaces(self, user_id: Optional[str]) -> list[str]:
|
|
83
77
|
"""Return workspace ids the caller can read.
|
|
84
78
|
|
|
@@ -47,6 +47,7 @@ from dataclasses import asdict, dataclass, field
|
|
|
47
47
|
from pathlib import Path
|
|
48
48
|
from typing import Any, Dict, List, Optional, Tuple
|
|
49
49
|
|
|
50
|
+
from latticeai.core.module_probe import module_available
|
|
50
51
|
from latticeai.core.quiet import quiet
|
|
51
52
|
from latticeai.services.process_audit import (
|
|
52
53
|
CommandConfirmationError,
|
|
@@ -59,6 +60,8 @@ from latticeai.services.setup_detection import (
|
|
|
59
60
|
detect_cuda,
|
|
60
61
|
detect_tools,
|
|
61
62
|
detect_wsl_from_text,
|
|
63
|
+
parse_windows_cpu_info,
|
|
64
|
+
windows_processor_features,
|
|
62
65
|
)
|
|
63
66
|
from latticeai.services.setup_detection import (
|
|
64
67
|
parse_windows_video_controllers as _parse_windows_video_controllers,
|
|
@@ -282,37 +285,23 @@ def _detect_cpu_details(prof_os: str) -> Tuple[str, int, int, List[str]]:
|
|
|
282
285
|
flags = line.split(":", 1)[-1].strip().lower().split()
|
|
283
286
|
elif prof_os == "windows":
|
|
284
287
|
raw = _run(["wmic", "cpu", "get", "Name,NumberOfCores,NumberOfLogicalProcessors", "/format:list"])
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
elif key == "NumberOfCores" and value.strip():
|
|
290
|
-
try:
|
|
291
|
-
physical = int(value.strip())
|
|
292
|
-
except ValueError:
|
|
293
|
-
quiet()
|
|
294
|
-
elif key == "NumberOfLogicalProcessors" and value.strip():
|
|
295
|
-
try:
|
|
296
|
-
logical = int(value.strip())
|
|
297
|
-
except ValueError:
|
|
298
|
-
quiet()
|
|
299
|
-
try:
|
|
300
|
-
import ctypes
|
|
301
|
-
kernel32 = ctypes.windll.kernel32 # type: ignore[attr-defined] # Windows-only
|
|
302
|
-
feature_map = {6: "sse", 10: "sse2", 13: "sse3", 19: "neon", 28: "rdrand"}
|
|
303
|
-
flags.extend(name for code, name in feature_map.items() if kernel32.IsProcessorFeaturePresent(code))
|
|
304
|
-
except Exception:
|
|
305
|
-
quiet()
|
|
288
|
+
model, physical, logical = parse_windows_cpu_info(
|
|
289
|
+
raw, model=model, physical_cores=physical, logical_cores=logical,
|
|
290
|
+
)
|
|
291
|
+
flags.extend(windows_processor_features())
|
|
306
292
|
interesting = {"avx", "avx2", "avx512f", "fma", "neon", "sse4_2", "sse", "sse2", "sse3", "rdrand"}
|
|
307
293
|
return model, physical, logical, sorted({flag for flag in flags if flag in interesting})
|
|
308
294
|
|
|
309
295
|
|
|
310
296
|
def _has_module(name: str) -> bool:
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
297
|
+
"""Importable *and loadable*.
|
|
298
|
+
|
|
299
|
+
``strict=True`` on purpose: this probe drives what the setup wizard offers
|
|
300
|
+
to install, and an ``mlx`` wheel built for another architecture has a
|
|
301
|
+
findable spec right up until it raises on load. Answering from the spec
|
|
302
|
+
alone would plan a runtime that cannot start.
|
|
303
|
+
"""
|
|
304
|
+
return module_available(name, strict=True)
|
|
316
305
|
|
|
317
306
|
|
|
318
307
|
def probe() -> SystemProfile:
|
|
@@ -10,6 +10,8 @@ from __future__ import annotations
|
|
|
10
10
|
import re
|
|
11
11
|
from typing import Dict, List, Tuple
|
|
12
12
|
|
|
13
|
+
from latticeai.services.model_catalog import _version_tuple as _catalog_version_tuple
|
|
14
|
+
|
|
13
15
|
# ── Model Catalog ─────────────────────────────────────────────────────────────
|
|
14
16
|
# (model_id, display_name, size_gb, tag, description, min_ram_gb)
|
|
15
17
|
# 11.2.0: 모든 repo id 를 2026-08-10 Hugging Face API 로 확인했다 — 존재 여부,
|
|
@@ -127,8 +129,9 @@ _BEST_MODEL_TIERS: Dict[str, List[Tuple[int, str]]] = {
|
|
|
127
129
|
}
|
|
128
130
|
|
|
129
131
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
+
|
|
133
|
+
#: The engine catalog owns this parse; the wizard compares the same strings.
|
|
134
|
+
_version_tuple = _catalog_version_tuple
|
|
132
135
|
|
|
133
136
|
|
|
134
137
|
def _catalog_row_family_version(row: Tuple[str, str, float, str, str, int]) -> Tuple[str, Tuple[int, ...]] | None:
|
|
@@ -21,6 +21,8 @@ from latticeai.services.setup_detection import (
|
|
|
21
21
|
detect_cuda,
|
|
22
22
|
detect_tools,
|
|
23
23
|
detect_wsl_from_text,
|
|
24
|
+
parse_windows_cpu_info,
|
|
25
|
+
windows_processor_features,
|
|
24
26
|
)
|
|
25
27
|
from latticeai.services.setup_detection import (
|
|
26
28
|
parse_windows_video_controllers as _parse_windows_video_controllers,
|
|
@@ -98,33 +100,10 @@ def _detect_cpu() -> Dict[str, Any]:
|
|
|
98
100
|
quiet()
|
|
99
101
|
elif platform.system() == "Windows":
|
|
100
102
|
raw = _cmd(["wmic", "cpu", "get", "Name,NumberOfCores,NumberOfLogicalProcessors", "/format:list"], timeout=5)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
elif key == "NumberOfCores" and value.strip():
|
|
106
|
-
try:
|
|
107
|
-
physical_cores = int(value.strip())
|
|
108
|
-
except ValueError:
|
|
109
|
-
quiet()
|
|
110
|
-
elif key == "NumberOfLogicalProcessors" and value.strip():
|
|
111
|
-
try:
|
|
112
|
-
logical_cores = int(value.strip())
|
|
113
|
-
except ValueError:
|
|
114
|
-
quiet()
|
|
115
|
-
try:
|
|
116
|
-
import ctypes
|
|
117
|
-
kernel32 = ctypes.windll.kernel32 # type: ignore[attr-defined] # Windows-only
|
|
118
|
-
feature_map = {
|
|
119
|
-
6: "sse",
|
|
120
|
-
10: "sse2",
|
|
121
|
-
13: "sse3",
|
|
122
|
-
19: "neon",
|
|
123
|
-
28: "rdrand",
|
|
124
|
-
}
|
|
125
|
-
flags.extend(name for code, name in feature_map.items() if kernel32.IsProcessorFeaturePresent(code))
|
|
126
|
-
except Exception:
|
|
127
|
-
quiet()
|
|
103
|
+
model, physical_cores, logical_cores = parse_windows_cpu_info(
|
|
104
|
+
raw, model=model, physical_cores=physical_cores, logical_cores=logical_cores,
|
|
105
|
+
)
|
|
106
|
+
flags.extend(windows_processor_features())
|
|
128
107
|
interesting = {"avx", "avx2", "avx512f", "fma", "neon", "sse4_2"}
|
|
129
108
|
if platform.system() == "Windows":
|
|
130
109
|
interesting.update({"sse", "sse2", "sse3", "rdrand"})
|
|
@@ -20,6 +20,8 @@ import shutil
|
|
|
20
20
|
from pathlib import Path
|
|
21
21
|
from typing import Any, Dict, List
|
|
22
22
|
|
|
23
|
+
from latticeai.core.module_probe import module_available as _module_available
|
|
24
|
+
|
|
23
25
|
OFFICIAL_DOWNLOADS: Dict[str, str] = {
|
|
24
26
|
"homebrew": "https://brew.sh",
|
|
25
27
|
"python": "https://www.python.org/downloads/",
|
|
@@ -149,11 +151,6 @@ def _which_detail(binary: str) -> Dict[str, Any]:
|
|
|
149
151
|
return {"installed": path is not None, "path": path}
|
|
150
152
|
|
|
151
153
|
|
|
152
|
-
def _module_available(module_name: str) -> bool:
|
|
153
|
-
import importlib.util
|
|
154
|
-
return importlib.util.find_spec(module_name) is not None
|
|
155
|
-
|
|
156
|
-
|
|
157
154
|
def _package_module(package: str) -> str:
|
|
158
155
|
return PACKAGE_MODULES.get(package, package.replace("-", "_").split("[", 1)[0])
|
|
159
156
|
|
|
@@ -8,15 +8,15 @@ plan and therefore nothing to confirm.
|
|
|
8
8
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
|
-
import json as _json
|
|
12
11
|
import sys
|
|
13
12
|
from typing import Any, Dict, List
|
|
14
13
|
|
|
14
|
+
from latticeai.core.sse import sse_frame
|
|
15
15
|
from latticeai.services.process_audit import command_plan_for_commands
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def _sse(data: Dict) -> str:
|
|
19
|
-
return
|
|
19
|
+
return sse_frame(None, data)
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def _action_commands(action: Dict[str, Any]) -> List[List[str]]:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ltcai",
|
|
3
|
-
"version": "11.5.
|
|
3
|
+
"version": "11.5.2",
|
|
4
4
|
"description": "Lattice AI — local-first Digital Brain that keeps your knowledge durable across any AI model.",
|
|
5
5
|
"homepage": "https://github.com/TaeSooPark-PTS/LatticeAI#readme",
|
|
6
6
|
"repository": {
|
|
@@ -52,7 +52,6 @@
|
|
|
52
52
|
"desktop:tauri:check": "cd src-tauri && cargo check",
|
|
53
53
|
"rust:test": "cd rust && cargo test --workspace",
|
|
54
54
|
"rust:lint": "cd rust && cargo fmt --all -- --check && cargo clippy --workspace --all-targets -- -D warnings",
|
|
55
|
-
"desktop:electron": "electron desktop/electron/main.cjs",
|
|
56
55
|
"package:vsix": "node scripts/build_vsix.mjs",
|
|
57
56
|
"check:screenshot-delta": "node scripts/run_python.mjs scripts/check_screenshot_pixel_delta.py",
|
|
58
57
|
"release:evidence": "node scripts/capture_release_evidence.mjs && node scripts/prune_release_evidence.mjs && npm run check:screenshot-delta",
|
|
@@ -124,7 +123,6 @@
|
|
|
124
123
|
"!src-tauri/gen/**",
|
|
125
124
|
"!src-tauri/target/",
|
|
126
125
|
"!src-tauri/target/**",
|
|
127
|
-
"desktop/electron/",
|
|
128
126
|
"docs/*.md",
|
|
129
127
|
"!docs/assets/",
|
|
130
128
|
"!docs/images/"
|
|
@@ -142,6 +140,7 @@
|
|
|
142
140
|
"@testing-library/react": "^16.3.2",
|
|
143
141
|
"@testing-library/user-event": "^14.6.1",
|
|
144
142
|
"@types/cytoscape": "^3.21.9",
|
|
143
|
+
"@types/node": "24.13.2",
|
|
145
144
|
"@types/react": "^19.2.17",
|
|
146
145
|
"@types/react-dom": "^19.2.3",
|
|
147
146
|
"@vitejs/plugin-react": "^6.0.2",
|
|
@@ -150,7 +149,6 @@
|
|
|
150
149
|
"class-variance-authority": "^0.7.1",
|
|
151
150
|
"clsx": "^2.1.1",
|
|
152
151
|
"cytoscape": "^3.34.0",
|
|
153
|
-
"electron": "^42.4.0",
|
|
154
152
|
"jsdom": "^29.1.1",
|
|
155
153
|
"lucide-react": "^1.17.0",
|
|
156
154
|
"openapi-fetch": "^0.17.0",
|
package/scripts/bump_version.py
CHANGED
|
@@ -25,7 +25,20 @@ REPO = Path(__file__).resolve().parents[1]
|
|
|
25
25
|
# it per crate — src-tauri's too, since 11.4.0, because the desktop shell now
|
|
26
26
|
# depends on lattice-host by path — and a lockfile left behind is a file the
|
|
27
27
|
# next `cargo build` silently rewrites underneath a tagged release.
|
|
28
|
-
|
|
28
|
+
#
|
|
29
|
+
# 11.5.2: this tuple listed only the three Phase-1 crates, so every bump since
|
|
30
|
+
# 11.5.0 left `lattice-agent`, `lattice-ingest` and `lattice-jobs` at the
|
|
31
|
+
# previous version in both lockfiles — and the first `cargo` invocation of the
|
|
32
|
+
# test suite rewrote them, dirtying the tree mid-release. Every workspace
|
|
33
|
+
# member belongs here.
|
|
34
|
+
RUST_CRATES = (
|
|
35
|
+
"lattice-agent",
|
|
36
|
+
"lattice-core",
|
|
37
|
+
"lattice-host",
|
|
38
|
+
"lattice-ingest",
|
|
39
|
+
"lattice-jobs",
|
|
40
|
+
"lattice-retrieval",
|
|
41
|
+
)
|
|
29
42
|
RUST_LOCKFILES = ("rust/Cargo.lock", "src-tauri/Cargo.lock")
|
|
30
43
|
|
|
31
44
|
# (path, kind, pattern) — pattern groups: (prefix, version)
|
|
@@ -6,7 +6,7 @@ const root = process.cwd();
|
|
|
6
6
|
const pkg = JSON.parse(readFileSync(path.join(root, "package.json"), "utf8"));
|
|
7
7
|
const version = pkg.version;
|
|
8
8
|
const releaseDir = `output/release/v${version}`;
|
|
9
|
-
const releaseTheme = "
|
|
9
|
+
const releaseTheme = "Tight Ship";
|
|
10
10
|
const title = `${version} — ${releaseTheme}`;
|
|
11
11
|
const escapedVersion = version.replaceAll(".", "\\.");
|
|
12
12
|
|
|
@@ -31,7 +31,7 @@ const REMOVED_MODULES = [
|
|
|
31
31
|
];
|
|
32
32
|
// Trees that must not import removed shims. static/app and node_modules are
|
|
33
33
|
// build output; src-tauri/target is compiler output.
|
|
34
|
-
const SOURCE_TREES = ["latticeai", "lattice_brain", "tests", "scripts", "bin"
|
|
34
|
+
const SOURCE_TREES = ["latticeai", "lattice_brain", "tests", "scripts", "bin"];
|
|
35
35
|
|
|
36
36
|
const failures = [];
|
|
37
37
|
|