ltcai 8.6.0 → 8.8.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 +40 -33
- package/docs/CHANGELOG.md +49 -1
- package/docs/COMMUNITY_AND_PLUGINS.md +7 -4
- package/docs/DEVELOPMENT.md +11 -9
- package/docs/LEGACY_COMPATIBILITY.md +33 -14
- package/docs/ONBOARDING.md +2 -2
- package/docs/TRUST_MODEL.md +1 -1
- package/docs/WHY_LATTICE.md +2 -2
- package/docs/kg-schema.md +1 -1
- package/kg_schema.py +1 -1
- package/knowledge_graph.py +2 -2
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/core.py +1 -1
- package/lattice_brain/ingestion.py +35 -20
- package/lattice_brain/runtime/agent_runtime.py +37 -3
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/app_factory.py +3 -3
- package/latticeai/core/config.py +14 -2
- package/latticeai/core/legacy_compatibility.py +36 -25
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/models/router.py +3 -2
- package/latticeai/runtime/persistence_runtime.py +1 -1
- package/latticeai/runtime/platform_services_runtime.py +1 -1
- package/latticeai/runtime/router_registration.py +11 -5
- package/latticeai/services/architecture_readiness.py +2 -2
- package/latticeai/services/memory_service.py +38 -7
- package/latticeai/services/model_runtime.py +52 -28
- package/latticeai/services/product_readiness.py +11 -11
- package/package.json +3 -2
- package/scripts/bump_version.py +2 -0
- package/scripts/check_python.py +25 -12
- 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 +11 -11
- package/static/app/assets/{Act-IrojDEWY.js → Act-C7K9wsO9.js} +1 -1
- package/static/app/assets/{Brain--VODO4HM.js → Brain-I1OSzxJu.js} +2 -2
- package/static/app/assets/Capture-B3V4_5Xp.js +1 -0
- package/static/app/assets/{Library-DrVto3cO.js → Library-Cgj-EF50.js} +1 -1
- package/static/app/assets/{System-DNrVbcMX.js → System-D1Lkei3I.js} +1 -1
- package/static/app/assets/index--P0ksosz.js +17 -0
- package/static/app/assets/index-DCh5AoXt.css +2 -0
- package/static/app/assets/{primitives-lz45ZfWY.js → primitives-BLqaKk5g.js} +1 -1
- package/static/app/assets/{textarea-C7uwJsxy.js → textarea-CVQkN2Tk.js} +1 -1
- package/static/app/index.html +2 -2
- package/static/sw.js +1 -1
- package/lattice_brain/_kg_common.py +0 -11
- package/lattice_brain/discovery.py +0 -11
- package/lattice_brain/documents.py +0 -11
- package/lattice_brain/identity.py +0 -11
- package/lattice_brain/ingest.py +0 -11
- package/lattice_brain/network.py +0 -11
- package/lattice_brain/projection.py +0 -11
- package/lattice_brain/provenance.py +0 -11
- package/lattice_brain/retrieval.py +0 -11
- package/lattice_brain/schema.py +0 -11
- package/lattice_brain/store.py +0 -11
- package/lattice_brain/write_master.py +0 -11
- package/latticeai/brain/__init__.py +0 -36
- package/latticeai/brain/_kg_common.py +0 -17
- package/latticeai/brain/context.py +0 -17
- package/latticeai/brain/conversations.py +0 -17
- package/latticeai/brain/discovery.py +0 -17
- package/latticeai/brain/documents.py +0 -17
- package/latticeai/brain/identity.py +0 -17
- package/latticeai/brain/ingest.py +0 -17
- package/latticeai/brain/memory.py +0 -17
- package/latticeai/brain/network.py +0 -17
- package/latticeai/brain/projection.py +0 -17
- package/latticeai/brain/provenance.py +0 -17
- package/latticeai/brain/retrieval.py +0 -17
- package/latticeai/brain/schema.py +0 -17
- package/latticeai/brain/store.py +0 -17
- package/latticeai/brain/write_master.py +0 -17
- package/latticeai/services/agent_runtime.py +0 -11
- package/static/app/assets/Capture-BUP_wJ7s.js +0 -1
- package/static/app/assets/index-BaaPtsLp.js +0 -16
- package/static/app/assets/index-_M5aCv21.css +0 -2
package/latticeai/app_factory.py
CHANGED
|
@@ -94,7 +94,7 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
94
94
|
from pydantic import BaseModel
|
|
95
95
|
|
|
96
96
|
from latticeai.models.router import LLMRouter, normalize_branding
|
|
97
|
-
from lattice_brain._kg_common import set_llm_router
|
|
97
|
+
from lattice_brain.graph._kg_common import set_llm_router
|
|
98
98
|
from lattice_brain.graph.schema import set_embed_dim
|
|
99
99
|
from latticeai.core.security import (
|
|
100
100
|
hash_password,
|
|
@@ -107,7 +107,7 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
107
107
|
enforce_rate_limit as _enforce_rate_limit,
|
|
108
108
|
)
|
|
109
109
|
from latticeai.core.audit import (
|
|
110
|
-
get_audit_log as _get_audit_log,
|
|
110
|
+
get_audit_log as _get_audit_log, # noqa: F401 - legacy server_app attr exported via locals()
|
|
111
111
|
classify_sensitive_message as _classify_sensitive_message,
|
|
112
112
|
build_sensitivity_report as _build_sensitivity_report,
|
|
113
113
|
build_admin_audit_report as _build_admin_audit_report,
|
|
@@ -992,7 +992,7 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
992
992
|
create_admin_router=create_admin_router,
|
|
993
993
|
require_admin=require_admin,
|
|
994
994
|
get_history=get_history,
|
|
995
|
-
get_audit_log=
|
|
995
|
+
get_audit_log=get_audit_log,
|
|
996
996
|
audit_file=AUDIT_FILE,
|
|
997
997
|
public_user=public_user,
|
|
998
998
|
load_vpc_config=load_vpc_config,
|
package/latticeai/core/config.py
CHANGED
|
@@ -43,7 +43,12 @@ def _bool(env: Mapping[str, str], key: str, default: bool = False) -> bool:
|
|
|
43
43
|
raw = env.get(key)
|
|
44
44
|
if raw is None:
|
|
45
45
|
return default
|
|
46
|
-
|
|
46
|
+
normalized = raw.strip().lower()
|
|
47
|
+
if normalized in {"1", "true", "yes", "on"}:
|
|
48
|
+
return True
|
|
49
|
+
if normalized in {"0", "false", "no", "off"}:
|
|
50
|
+
return False
|
|
51
|
+
return default
|
|
47
52
|
|
|
48
53
|
|
|
49
54
|
def _int(env: Mapping[str, str], key: str, default: int) -> int:
|
|
@@ -56,6 +61,13 @@ def _int(env: Mapping[str, str], key: str, default: int) -> int:
|
|
|
56
61
|
return default
|
|
57
62
|
|
|
58
63
|
|
|
64
|
+
def _port(env: Mapping[str, str], key: str, default: int) -> int:
|
|
65
|
+
port = _int(env, key, default)
|
|
66
|
+
if 1 <= port <= 65535:
|
|
67
|
+
return port
|
|
68
|
+
return default
|
|
69
|
+
|
|
70
|
+
|
|
59
71
|
@dataclass(frozen=True)
|
|
60
72
|
class Config:
|
|
61
73
|
"""Everything a caller must know about app-level settings.
|
|
@@ -146,7 +158,7 @@ class Config:
|
|
|
146
158
|
is_public = app_mode == "public"
|
|
147
159
|
|
|
148
160
|
host = _value(env, "LATTICEAI_HOST", "127.0.0.1")
|
|
149
|
-
port =
|
|
161
|
+
port = _port(env, "LATTICEAI_PORT", 4825)
|
|
150
162
|
network_exposed = not host_is_loopback(host)
|
|
151
163
|
|
|
152
164
|
cors_extra = [item.strip() for item in _value(env, "LATTICEAI_CORS_ALLOWED_ORIGINS", "").split(",") if item.strip()]
|
|
@@ -14,7 +14,7 @@ from pathlib import Path
|
|
|
14
14
|
from typing import Any, Dict, List
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
LEGACY_COMPATIBILITY_VERSION = "8.
|
|
17
|
+
LEGACY_COMPATIBILITY_VERSION = "8.8.0"
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
@dataclass(frozen=True)
|
|
@@ -43,7 +43,7 @@ LEGACY_SHIMS: List[LegacyShim] = [
|
|
|
43
43
|
LegacyShim(
|
|
44
44
|
path="knowledge_graph.py",
|
|
45
45
|
owner="lattice_brain.graph",
|
|
46
|
-
replacement="from lattice_brain.store import KnowledgeGraphStore",
|
|
46
|
+
replacement="from lattice_brain.graph.store import KnowledgeGraphStore",
|
|
47
47
|
reason="Historical scripts imported the graph store from the repo root.",
|
|
48
48
|
removal_phase="major-release-after-8.x",
|
|
49
49
|
),
|
|
@@ -57,7 +57,7 @@ LEGACY_SHIMS: List[LegacyShim] = [
|
|
|
57
57
|
LegacyShim(
|
|
58
58
|
path="kg_schema.py",
|
|
59
59
|
owner="lattice_brain.graph.schema",
|
|
60
|
-
replacement="from lattice_brain.schema import ...",
|
|
60
|
+
replacement="from lattice_brain.graph.schema import ...",
|
|
61
61
|
reason="Historical graph schema tests and tools referenced root schema symbols.",
|
|
62
62
|
removal_phase="major-release-after-8.x",
|
|
63
63
|
),
|
|
@@ -96,53 +96,59 @@ LEGACY_SHIMS: List[LegacyShim] = [
|
|
|
96
96
|
reason="Local folder ingestion integrations used the root local knowledge API.",
|
|
97
97
|
removal_phase="requires-api-route-migration",
|
|
98
98
|
),
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
# Shim layers that have completed their compatibility window and were
|
|
102
|
+
# physically deleted. Kept in the report so consumers (docs, tests, release
|
|
103
|
+
# notes) can tell "removed on purpose" apart from "missing by accident".
|
|
104
|
+
# Removing the internal-only layers is the first step of extracting
|
|
105
|
+
# ``lattice_brain`` as a standalone Brain Core package: the package now has
|
|
106
|
+
# exactly one import surface (``lattice_brain.*`` physical paths).
|
|
107
|
+
REMOVED_SHIMS: List[LegacyShim] = [
|
|
99
108
|
LegacyShim(
|
|
100
109
|
path="lattice_brain/store.py",
|
|
101
110
|
owner="lattice_brain.graph.store",
|
|
102
111
|
replacement="from lattice_brain.graph.store import KnowledgeGraphStore",
|
|
103
|
-
reason="Pre-graph-package
|
|
104
|
-
removal_phase="
|
|
112
|
+
reason="Pre-graph-package flat modules were internal-only; removed in 8.8.0.",
|
|
113
|
+
removal_phase="removed-8.8.0",
|
|
105
114
|
layer="brain-flat",
|
|
115
|
+
status="removed",
|
|
106
116
|
),
|
|
107
117
|
LegacyShim(
|
|
108
118
|
path="lattice_brain/ingest.py",
|
|
109
119
|
owner="lattice_brain.graph.ingest",
|
|
110
120
|
replacement="from lattice_brain.graph.ingest import KnowledgeGraphIngestMixin",
|
|
111
|
-
reason="Pre-graph-package
|
|
112
|
-
removal_phase="
|
|
121
|
+
reason="Pre-graph-package flat modules were internal-only; removed in 8.8.0.",
|
|
122
|
+
removal_phase="removed-8.8.0",
|
|
113
123
|
layer="brain-flat",
|
|
124
|
+
status="removed",
|
|
114
125
|
),
|
|
115
126
|
LegacyShim(
|
|
116
127
|
path="lattice_brain/retrieval.py",
|
|
117
128
|
owner="lattice_brain.graph.retrieval",
|
|
118
129
|
replacement="from lattice_brain.graph.retrieval import KnowledgeGraphRetrievalMixin",
|
|
119
|
-
reason="Pre-graph-package
|
|
120
|
-
removal_phase="
|
|
130
|
+
reason="Pre-graph-package flat modules were internal-only; removed in 8.8.0.",
|
|
131
|
+
removal_phase="removed-8.8.0",
|
|
121
132
|
layer="brain-flat",
|
|
133
|
+
status="removed",
|
|
122
134
|
),
|
|
123
135
|
LegacyShim(
|
|
124
|
-
path="latticeai/brain/
|
|
125
|
-
owner="lattice_brain
|
|
126
|
-
replacement="
|
|
127
|
-
reason="The deprecated latticeai.brain namespace
|
|
128
|
-
removal_phase="
|
|
129
|
-
layer="deprecated-namespace",
|
|
130
|
-
),
|
|
131
|
-
LegacyShim(
|
|
132
|
-
path="latticeai/brain/ingest.py",
|
|
133
|
-
owner="lattice_brain.graph.ingest",
|
|
134
|
-
replacement="from lattice_brain.graph.ingest import KnowledgeGraphIngestMixin",
|
|
135
|
-
reason="The deprecated latticeai.brain namespace remains for package users.",
|
|
136
|
-
removal_phase="major-release-after-8.x",
|
|
136
|
+
path="latticeai/brain/",
|
|
137
|
+
owner="lattice_brain",
|
|
138
|
+
replacement="import lattice_brain",
|
|
139
|
+
reason="The deprecated latticeai.brain namespace completed its window; removed in 8.8.0.",
|
|
140
|
+
removal_phase="removed-8.8.0",
|
|
137
141
|
layer="deprecated-namespace",
|
|
142
|
+
status="removed",
|
|
138
143
|
),
|
|
139
144
|
LegacyShim(
|
|
140
145
|
path="latticeai/services/agent_runtime.py",
|
|
141
146
|
owner="lattice_brain.runtime.agent_runtime",
|
|
142
147
|
replacement="from lattice_brain.runtime.agent_runtime import AgentRuntime",
|
|
143
|
-
reason="
|
|
144
|
-
removal_phase="
|
|
148
|
+
reason="The service-layer alias completed its window; removed in 8.8.0.",
|
|
149
|
+
removal_phase="removed-8.8.0",
|
|
145
150
|
layer="service-alias",
|
|
151
|
+
status="removed",
|
|
146
152
|
),
|
|
147
153
|
]
|
|
148
154
|
|
|
@@ -152,23 +158,28 @@ def legacy_shim_report(root: Path | None = None) -> Dict[str, Any]:
|
|
|
152
158
|
root = Path(__file__).resolve().parents[2]
|
|
153
159
|
entries = [shim.as_dict() for shim in LEGACY_SHIMS]
|
|
154
160
|
missing = [shim.path for shim in LEGACY_SHIMS if not (root / shim.path).exists()]
|
|
161
|
+
lingering = [shim.path for shim in REMOVED_SHIMS if (root / shim.path).exists()]
|
|
155
162
|
phases = sorted({shim.removal_phase for shim in LEGACY_SHIMS})
|
|
156
163
|
layers = sorted({shim.layer for shim in LEGACY_SHIMS})
|
|
157
164
|
return {
|
|
158
165
|
"schema_version": "legacy-compatibility/v1",
|
|
159
166
|
"version_target": LEGACY_COMPATIBILITY_VERSION,
|
|
160
|
-
"status": "managed" if not missing else "incomplete",
|
|
167
|
+
"status": "managed" if not missing and not lingering else "incomplete",
|
|
161
168
|
"remaining_count": len(LEGACY_SHIMS),
|
|
169
|
+
"removed_count": len(REMOVED_SHIMS),
|
|
162
170
|
"missing": missing,
|
|
171
|
+
"lingering": lingering,
|
|
163
172
|
"removal_phases": phases,
|
|
164
173
|
"layers": layers,
|
|
165
174
|
"shims": entries,
|
|
175
|
+
"removed": [shim.as_dict() for shim in REMOVED_SHIMS],
|
|
166
176
|
}
|
|
167
177
|
|
|
168
178
|
|
|
169
179
|
__all__ = [
|
|
170
180
|
"LEGACY_COMPATIBILITY_VERSION",
|
|
171
181
|
"LEGACY_SHIMS",
|
|
182
|
+
"REMOVED_SHIMS",
|
|
172
183
|
"LegacyShim",
|
|
173
184
|
"legacy_shim_report",
|
|
174
185
|
]
|
|
@@ -49,7 +49,7 @@ __all__ = [
|
|
|
49
49
|
"remove_skill_directory",
|
|
50
50
|
]
|
|
51
51
|
|
|
52
|
-
WORKSPACE_OS_VERSION = "8.
|
|
52
|
+
WORKSPACE_OS_VERSION = "8.8.0"
|
|
53
53
|
|
|
54
54
|
# Workspace types separate single-user Personal workspaces from shared
|
|
55
55
|
# Organization workspaces. Both keep the same local-first JSON store; the type
|
|
@@ -13,8 +13,9 @@ import time
|
|
|
13
13
|
from dataclasses import dataclass
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
|
|
16
|
-
#
|
|
17
|
-
|
|
16
|
+
# Default Gemma 4 assistant drafting to MTP without overriding an operator's
|
|
17
|
+
# explicit MLX runtime choice.
|
|
18
|
+
os.environ.setdefault("MLX_VLM_DRAFT_KIND", "mtp")
|
|
18
19
|
|
|
19
20
|
from concurrent.futures import ThreadPoolExecutor
|
|
20
21
|
from typing import AsyncIterator, Dict, Optional, Tuple, List
|
|
@@ -27,7 +27,7 @@ def build_persistence_runtime(
|
|
|
27
27
|
import os
|
|
28
28
|
from pathlib import Path
|
|
29
29
|
|
|
30
|
-
from lattice_brain.identity import DeviceIdentity
|
|
30
|
+
from lattice_brain.graph.identity import DeviceIdentity
|
|
31
31
|
from lattice_brain.ingestion import IngestionPipeline
|
|
32
32
|
from lattice_brain.portability import KGPortabilityService
|
|
33
33
|
from latticeai.core.agent_registry import AgentRegistry
|
|
@@ -30,7 +30,7 @@ def build_brain_network(
|
|
|
30
30
|
) -> Any:
|
|
31
31
|
"""Construct peer sync/network service for brain portability routes."""
|
|
32
32
|
|
|
33
|
-
from lattice_brain.network import BrainNetwork
|
|
33
|
+
from lattice_brain.graph.network import BrainNetwork
|
|
34
34
|
|
|
35
35
|
return BrainNetwork(
|
|
36
36
|
identity=identity,
|
|
@@ -162,16 +162,22 @@ def build_auth_admin_security_router_bundle(
|
|
|
162
162
|
append_audit_event=append_audit_event,
|
|
163
163
|
)
|
|
164
164
|
|
|
165
|
-
def
|
|
165
|
+
def audit_log_events_safe() -> list[dict[str, Any]]:
|
|
166
166
|
try:
|
|
167
|
-
return get_audit_log(
|
|
167
|
+
return get_audit_log()
|
|
168
|
+
except TypeError:
|
|
169
|
+
try:
|
|
170
|
+
return get_audit_log(audit_file)
|
|
171
|
+
except Exception as exc: # pragma: no cover - defensive legacy behavior
|
|
172
|
+
logger.warning("security audit events load failed: %s", exc)
|
|
173
|
+
return []
|
|
168
174
|
except Exception as exc: # pragma: no cover - defensive legacy behavior
|
|
169
175
|
logger.warning("security audit events load failed: %s", exc)
|
|
170
176
|
return []
|
|
171
177
|
|
|
172
178
|
def security_list_uploaded_files() -> list[dict[str, Any]]:
|
|
173
179
|
files: list[dict[str, Any]] = []
|
|
174
|
-
for idx, event in enumerate(
|
|
180
|
+
for idx, event in enumerate(audit_log_events_safe()):
|
|
175
181
|
if event.get("event_type") != "document_upload":
|
|
176
182
|
continue
|
|
177
183
|
files.append({
|
|
@@ -191,7 +197,7 @@ def build_auth_admin_security_router_bundle(
|
|
|
191
197
|
security_router = create_security_router(
|
|
192
198
|
require_admin=require_admin,
|
|
193
199
|
get_history=get_history,
|
|
194
|
-
get_audit_events=
|
|
200
|
+
get_audit_events=audit_log_events_safe,
|
|
195
201
|
classify_sensitive_message=classify_sensitive_message,
|
|
196
202
|
build_sensitivity_report=build_sensitivity_report,
|
|
197
203
|
list_uploaded_files=security_list_uploaded_files,
|
|
@@ -205,7 +211,7 @@ def build_auth_admin_security_router_bundle(
|
|
|
205
211
|
"security_router": security_router,
|
|
206
212
|
"_graph_stats_safe": graph_stats_safe,
|
|
207
213
|
"_product_hardening_status": product_hardening_status,
|
|
208
|
-
"_security_audit_events_safe":
|
|
214
|
+
"_security_audit_events_safe": audit_log_events_safe,
|
|
209
215
|
"_security_list_uploaded_files": security_list_uploaded_files,
|
|
210
216
|
}
|
|
211
217
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Machine-checkable architecture readiness gates for release work.
|
|
2
2
|
|
|
3
|
-
8.
|
|
3
|
+
8.8.0 keeps the major architecture priorities under an explicit release
|
|
4
4
|
contract while product maturity work reduces visible beta seams. AgentRuntime, ToolRegistry,
|
|
5
5
|
central Config, decomposed server runtime, and Knowledge Graph stabilization
|
|
6
6
|
must remain discoverable, ordered, and backed by tests before the release can be
|
|
@@ -17,7 +17,7 @@ from typing import Any, Dict, List
|
|
|
17
17
|
from latticeai.core.legacy_compatibility import legacy_shim_report
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
ARCHITECTURE_VERSION_TARGET = "8.
|
|
20
|
+
ARCHITECTURE_VERSION_TARGET = "8.8.0"
|
|
21
21
|
|
|
22
22
|
PREFERRED_REFACTORING_ORDER = [
|
|
23
23
|
"agent-runtime",
|
|
@@ -533,6 +533,10 @@ class MemoryService:
|
|
|
533
533
|
"title": item.get("title"),
|
|
534
534
|
"snippet": item.get("snippet"),
|
|
535
535
|
"score": item.get("score", 0),
|
|
536
|
+
# Evidence explainability: why this item was recalled, so the
|
|
537
|
+
# citation UI can show the matched terms instead of a bare score.
|
|
538
|
+
"matched_terms": item.get("matched_terms") or [],
|
|
539
|
+
"confidence": item.get("confidence") or "low",
|
|
536
540
|
}
|
|
537
541
|
for item in recall.get("results", [])[: max(1, min(limit, 8))]
|
|
538
542
|
]
|
|
@@ -616,15 +620,17 @@ class MemoryService:
|
|
|
616
620
|
q = str(query or "").strip()
|
|
617
621
|
query_tokens = [tok for tok in q.lower().split() if tok]
|
|
618
622
|
|
|
619
|
-
def
|
|
623
|
+
def _matched_terms(*texts: Any) -> List[str]:
|
|
624
|
+
haystack = " ".join(str(t or "") for t in texts).lower()
|
|
625
|
+
return [tok for tok in query_tokens if tok in haystack]
|
|
626
|
+
|
|
627
|
+
def _lexical_score(matched: List[str]) -> float:
|
|
620
628
|
# Honest, comparable relevance: fraction of query tokens present.
|
|
621
629
|
# Both tiers share this scorer so the cross-tier ranking is real,
|
|
622
630
|
# not an artifact of per-tier constants.
|
|
623
631
|
if not query_tokens:
|
|
624
632
|
return 0.0
|
|
625
|
-
|
|
626
|
-
hits = sum(1 for tok in query_tokens if tok in haystack)
|
|
627
|
-
return round(hits / len(query_tokens), 4)
|
|
633
|
+
return round(len(matched) / len(query_tokens), 4)
|
|
628
634
|
|
|
629
635
|
results: List[Dict[str, Any]] = []
|
|
630
636
|
|
|
@@ -633,13 +639,15 @@ class MemoryService:
|
|
|
633
639
|
except Exception:
|
|
634
640
|
mem = []
|
|
635
641
|
for m in mem:
|
|
642
|
+
matched = _matched_terms(m.get("content"), " ".join(m.get("tags") or []), m.get("kind"))
|
|
636
643
|
results.append({
|
|
637
644
|
"source": "workspace",
|
|
638
645
|
"id": m.get("id"),
|
|
639
646
|
"title": (m.get("kind") or "memory"),
|
|
640
647
|
"snippet": str(m.get("content") or "")[:240],
|
|
641
648
|
"kind": m.get("kind"),
|
|
642
|
-
"score": _lexical_score(
|
|
649
|
+
"score": _lexical_score(matched),
|
|
650
|
+
"matched_terms": matched,
|
|
643
651
|
"tags": m.get("tags") or [],
|
|
644
652
|
})
|
|
645
653
|
|
|
@@ -650,17 +658,40 @@ class MemoryService:
|
|
|
650
658
|
except Exception:
|
|
651
659
|
hits = []
|
|
652
660
|
for hit in hits[:limit]:
|
|
661
|
+
matched = _matched_terms(hit.get("title"), hit.get("name"), hit.get("summary"), hit.get("content"))
|
|
653
662
|
results.append({
|
|
654
663
|
"source": "graph",
|
|
655
664
|
"id": hit.get("id") or hit.get("node_id"),
|
|
656
665
|
"title": hit.get("title") or hit.get("name") or "node",
|
|
657
666
|
"snippet": str(hit.get("summary") or hit.get("content") or "")[:240],
|
|
658
667
|
"kind": hit.get("type") or "node",
|
|
659
|
-
"score": _lexical_score(
|
|
668
|
+
"score": _lexical_score(matched),
|
|
669
|
+
"matched_terms": matched,
|
|
660
670
|
})
|
|
661
671
|
|
|
672
|
+
# Quality gate: when at least one result carries real lexical evidence,
|
|
673
|
+
# zero-score rows are noise relative to it and are dropped. When nothing
|
|
674
|
+
# scores (e.g. tokenization mismatch), everything is kept so the tiers'
|
|
675
|
+
# own search filters still decide — the gate never empties a recall.
|
|
676
|
+
candidates = len(results)
|
|
677
|
+
if query_tokens and any(r.get("score", 0) > 0 for r in results):
|
|
678
|
+
results = [r for r in results if r.get("score", 0) > 0]
|
|
679
|
+
for r in results:
|
|
680
|
+
r["confidence"] = "high" if r.get("score", 0) >= 0.65 else "medium" if r.get("score", 0) >= 0.3 else "low"
|
|
681
|
+
|
|
662
682
|
results.sort(key=lambda r: r.get("score", 0), reverse=True)
|
|
663
|
-
return {
|
|
683
|
+
return {
|
|
684
|
+
"query": q,
|
|
685
|
+
"results": results[: max(1, min(limit, 100))],
|
|
686
|
+
"count": len(results),
|
|
687
|
+
"source": "live",
|
|
688
|
+
"quality_gate": {
|
|
689
|
+
"candidates": candidates,
|
|
690
|
+
"passed": len(results),
|
|
691
|
+
"filtered": candidates - len(results),
|
|
692
|
+
"gate": "lexical-evidence/v1",
|
|
693
|
+
},
|
|
694
|
+
}
|
|
664
695
|
|
|
665
696
|
# ── inspect a single tier ─────────────────────────────────────────────
|
|
666
697
|
def inspect(self, source: str, *, user_email: Optional[str] = None, workspace_id: Optional[str] = None, limit: int = 50) -> Dict[str, Any]:
|
|
@@ -16,6 +16,7 @@ import shutil
|
|
|
16
16
|
import time
|
|
17
17
|
import urllib.error
|
|
18
18
|
import urllib.request
|
|
19
|
+
import warnings
|
|
19
20
|
from pathlib import Path
|
|
20
21
|
from typing import AsyncIterator, Dict, List, Optional
|
|
21
22
|
|
|
@@ -100,6 +101,21 @@ class ModelRuntimeState:
|
|
|
100
101
|
self.get_user_api_key = _missing_user_api_key
|
|
101
102
|
|
|
102
103
|
def sync_to_module_globals(self):
|
|
104
|
+
"""Sync to bare module globals for legacy external importers.
|
|
105
|
+
|
|
106
|
+
This is a compatibility surface only. Internal code should use STATE.
|
|
107
|
+
Emits DeprecationWarning (future removal in major after 8.x).
|
|
108
|
+
"""
|
|
109
|
+
warnings.warn(
|
|
110
|
+
"sync_to_module_globals is a legacy compatibility shim and will be removed "
|
|
111
|
+
"after 8.x. Use latticeai.services.model_runtime.STATE or injected context instead.",
|
|
112
|
+
DeprecationWarning,
|
|
113
|
+
stacklevel=2,
|
|
114
|
+
)
|
|
115
|
+
self._sync_globals()
|
|
116
|
+
|
|
117
|
+
def _sync_globals(self) -> None:
|
|
118
|
+
"""Internal no-warning sync used at init."""
|
|
103
119
|
global router, APP_MODE, DEFAULT_HOST, DEFAULT_PORT, DATA_DIR, BASE_DIR
|
|
104
120
|
global ENABLE_TELEGRAM, ENABLE_GRAPH, AUTOLOAD_MODELS, MODEL_IDLE_UNLOAD_SECONDS
|
|
105
121
|
global ALLOW_LOCAL_MODELS, REQUIRE_AUTH, INVITE_GATE_ENABLED, ALLOW_PLAINTEXT_API_KEYS
|
|
@@ -128,7 +144,7 @@ class ModelRuntimeState:
|
|
|
128
144
|
get_user_api_key = self.get_user_api_key
|
|
129
145
|
|
|
130
146
|
STATE = ModelRuntimeState()
|
|
131
|
-
STATE.
|
|
147
|
+
STATE._sync_globals() # initial no-warning; public API warns on explicit legacy syncs
|
|
132
148
|
|
|
133
149
|
# Configured by server_app.configure_model_runtime during app assembly.
|
|
134
150
|
|
|
@@ -141,7 +157,9 @@ def _env_bool(key: str, default: bool = False) -> bool:
|
|
|
141
157
|
|
|
142
158
|
|
|
143
159
|
def _download_allowed(allow_download: bool = False) -> bool:
|
|
144
|
-
|
|
160
|
+
# Prefer STATE (the source of truth) over bare module global for internal logic.
|
|
161
|
+
autoload = getattr(STATE, "AUTOLOAD_MODELS", AUTOLOAD_MODELS)
|
|
162
|
+
return bool(allow_download) or _env_bool("LATTICEAI_ALLOW_MODEL_DOWNLOADS", default=False) or bool(autoload)
|
|
145
163
|
|
|
146
164
|
|
|
147
165
|
def _download_block(provider: str, model_name: str) -> None:
|
|
@@ -204,7 +222,7 @@ def configure_model_runtime(**deps) -> None:
|
|
|
204
222
|
elif key == "get_user_api_key":
|
|
205
223
|
STATE.get_user_api_key = value
|
|
206
224
|
|
|
207
|
-
STATE.
|
|
225
|
+
STATE._sync_globals() # wiring path uses internal (no spurious deprecation in normal startup)
|
|
208
226
|
|
|
209
227
|
|
|
210
228
|
# Catalog data + version-dedup helpers live in ``model_catalog``; re-exported
|
|
@@ -727,7 +745,8 @@ def engine_installed(engine: str) -> bool:
|
|
|
727
745
|
return False
|
|
728
746
|
|
|
729
747
|
def engine_status() -> List[Dict]:
|
|
730
|
-
|
|
748
|
+
r = getattr(STATE, "router", None) or router
|
|
749
|
+
cloud_models = r.detected_cloud_models() if r else []
|
|
731
750
|
cloud_by_provider = {}
|
|
732
751
|
for model in cloud_models:
|
|
733
752
|
cloud_by_provider.setdefault(model["provider"], []).append(model)
|
|
@@ -887,37 +906,41 @@ def engine_status() -> List[Dict]:
|
|
|
887
906
|
return engines
|
|
888
907
|
|
|
889
908
|
def runtime_features() -> Dict:
|
|
909
|
+
# Read from STATE object (central) for implementation; bare globals kept only
|
|
910
|
+
# for external legacy consumers who import names directly from this module.
|
|
911
|
+
s = STATE
|
|
912
|
+
r = getattr(s, "router", None) or router
|
|
890
913
|
return {
|
|
891
|
-
"mode": APP_MODE,
|
|
892
|
-
"public": IS_PUBLIC_MODE,
|
|
893
|
-
"host": DEFAULT_HOST,
|
|
894
|
-
"port": DEFAULT_PORT,
|
|
895
|
-
"data_dir": str(DATA_DIR),
|
|
896
|
-
"telegram_enabled": ENABLE_TELEGRAM,
|
|
897
|
-
"graph_enabled": ENABLE_GRAPH,
|
|
898
|
-
"autoload_models": AUTOLOAD_MODELS,
|
|
899
|
-
"model_idle_unload_seconds": MODEL_IDLE_UNLOAD_SECONDS,
|
|
900
|
-
"model_memory_policy":
|
|
901
|
-
"allow_local_models": ALLOW_LOCAL_MODELS,
|
|
914
|
+
"mode": s.APP_MODE,
|
|
915
|
+
"public": s.IS_PUBLIC_MODE,
|
|
916
|
+
"host": s.DEFAULT_HOST,
|
|
917
|
+
"port": s.DEFAULT_PORT,
|
|
918
|
+
"data_dir": str(s.DATA_DIR),
|
|
919
|
+
"telegram_enabled": s.ENABLE_TELEGRAM,
|
|
920
|
+
"graph_enabled": s.ENABLE_GRAPH,
|
|
921
|
+
"autoload_models": s.AUTOLOAD_MODELS,
|
|
922
|
+
"model_idle_unload_seconds": s.MODEL_IDLE_UNLOAD_SECONDS,
|
|
923
|
+
"model_memory_policy": r.model_memory_policy() if r else None,
|
|
924
|
+
"allow_local_models": s.ALLOW_LOCAL_MODELS,
|
|
902
925
|
"security": {
|
|
903
|
-
"host": DEFAULT_HOST,
|
|
904
|
-
"require_auth": REQUIRE_AUTH,
|
|
905
|
-
"invite_gate_enabled": INVITE_GATE_ENABLED,
|
|
906
|
-
"keyring_available": keyring is not None,
|
|
907
|
-
"plaintext_api_keys_allowed": ALLOW_PLAINTEXT_API_KEYS,
|
|
908
|
-
"cors_allow_network": CORS_ALLOW_NETWORK,
|
|
926
|
+
"host": s.DEFAULT_HOST,
|
|
927
|
+
"require_auth": s.REQUIRE_AUTH,
|
|
928
|
+
"invite_gate_enabled": s.INVITE_GATE_ENABLED,
|
|
929
|
+
"keyring_available": s.keyring is not None,
|
|
930
|
+
"plaintext_api_keys_allowed": s.ALLOW_PLAINTEXT_API_KEYS,
|
|
931
|
+
"cors_allow_network": s.CORS_ALLOW_NETWORK,
|
|
909
932
|
},
|
|
910
|
-
"default_model": PUBLIC_MODEL if IS_PUBLIC_MODE else LOCAL_MODEL,
|
|
933
|
+
"default_model": s.PUBLIC_MODEL if s.IS_PUBLIC_MODE else s.LOCAL_MODEL,
|
|
911
934
|
"local_only_features": {
|
|
912
|
-
"mlx": ALLOW_LOCAL_MODELS and not IS_PUBLIC_MODE,
|
|
913
|
-
"telegram_bridge": ENABLE_TELEGRAM,
|
|
914
|
-
"desktop_chrome_bridge": not IS_PUBLIC_MODE,
|
|
915
|
-
"computer_use_bridge": not IS_PUBLIC_MODE,
|
|
935
|
+
"mlx": s.ALLOW_LOCAL_MODELS and not s.IS_PUBLIC_MODE,
|
|
936
|
+
"telegram_bridge": s.ENABLE_TELEGRAM,
|
|
937
|
+
"desktop_chrome_bridge": not s.IS_PUBLIC_MODE,
|
|
938
|
+
"computer_use_bridge": not s.IS_PUBLIC_MODE,
|
|
916
939
|
},
|
|
917
940
|
"public_features": {
|
|
918
941
|
"web_ui": True,
|
|
919
942
|
"openai_compatible_models": True,
|
|
920
|
-
"persistent_data_dir": str(DATA_DIR),
|
|
943
|
+
"persistent_data_dir": str(s.DATA_DIR),
|
|
921
944
|
},
|
|
922
945
|
}
|
|
923
946
|
|
|
@@ -1099,7 +1122,8 @@ async def _probe_cloud_model(model_ref: str) -> Dict[str, object]:
|
|
|
1099
1122
|
|
|
1100
1123
|
async def verify_cloud_models(force: bool = False, provider_filter: Optional[str] = None) -> Dict[str, Dict]:
|
|
1101
1124
|
now = time.time()
|
|
1102
|
-
|
|
1125
|
+
r = getattr(STATE, "router", None) or router
|
|
1126
|
+
cloud_items = [item for item in (r.detected_cloud_models() if r else []) if item.get("tag") == "cloud"]
|
|
1103
1127
|
if provider_filter:
|
|
1104
1128
|
cloud_items = [item for item in cloud_items if item.get("provider") == provider_filter]
|
|
1105
1129
|
|
|
@@ -18,7 +18,7 @@ from typing import Any, Dict, List
|
|
|
18
18
|
|
|
19
19
|
from latticeai.services.architecture_readiness import architecture_readiness
|
|
20
20
|
|
|
21
|
-
PRODUCT_VERSION_TARGET = "8.
|
|
21
|
+
PRODUCT_VERSION_TARGET = "8.8.0"
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
@dataclass(frozen=True)
|
|
@@ -76,10 +76,10 @@ PRODUCT_GATES: List[ProductGate] = [
|
|
|
76
76
|
evidence=[
|
|
77
77
|
"package.json::release:artifacts",
|
|
78
78
|
"package.json::release:validate",
|
|
79
|
-
"README.md::dist/ltcai-8.
|
|
80
|
-
"README.md::dist/ltcai-8.
|
|
81
|
-
"README.md::dist/ltcai-8.
|
|
82
|
-
"README.md::ltcai-8.
|
|
79
|
+
"README.md::dist/ltcai-8.8.0-py3-none-any.whl",
|
|
80
|
+
"README.md::dist/ltcai-8.8.0.tar.gz",
|
|
81
|
+
"README.md::dist/ltcai-8.8.0.vsix",
|
|
82
|
+
"README.md::ltcai-8.8.0.tgz",
|
|
83
83
|
"scripts/validate_release_artifacts.py",
|
|
84
84
|
"scripts/release_smoke.py",
|
|
85
85
|
"Dockerfile",
|
|
@@ -95,12 +95,12 @@ PRODUCT_GATES: List[ProductGate] = [
|
|
|
95
95
|
title="Release story is documented and honest",
|
|
96
96
|
evidence=[
|
|
97
97
|
"README.md",
|
|
98
|
-
"README.md::The current release is **8.
|
|
99
|
-
"SECURITY.md::8.
|
|
100
|
-
"vscode-extension/README.md::**8.
|
|
101
|
-
"docs/CHANGELOG.md::## [8.
|
|
98
|
+
"README.md::The current release is **8.8.0",
|
|
99
|
+
"SECURITY.md::8.8.x (latest)",
|
|
100
|
+
"vscode-extension/README.md::**8.8.0",
|
|
101
|
+
"docs/CHANGELOG.md::## [8.8.0]",
|
|
102
102
|
"FEATURE_STATUS.md",
|
|
103
|
-
"RELEASE_NOTES_v8.
|
|
103
|
+
"RELEASE_NOTES_v8.8.0.md",
|
|
104
104
|
"latticeai/core/agent.py::SingleAgentRuntime",
|
|
105
105
|
"latticeai/core/agent.py::AgentRuntime = SingleAgentRuntime",
|
|
106
106
|
"lattice_brain/runtime/contracts.py::runtime-boundary/v1",
|
|
@@ -115,7 +115,7 @@ PRODUCT_GATES: List[ProductGate] = [
|
|
|
115
115
|
id="ecosystem-path",
|
|
116
116
|
title="Community and plugin growth path is explicit",
|
|
117
117
|
evidence=[
|
|
118
|
-
"docs/COMMUNITY_AND_PLUGINS.md::8.
|
|
118
|
+
"docs/COMMUNITY_AND_PLUGINS.md::8.8.0",
|
|
119
119
|
"docs/PLUGIN_SDK.md",
|
|
120
120
|
"plugins/README.md",
|
|
121
121
|
"plugins/hello-world/plugin.json",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ltcai",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.8.0",
|
|
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": {
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"build:assets": "vite build && node scripts/build_frontend_assets.mjs",
|
|
24
24
|
"build:python": "node scripts/run_python.mjs -m build",
|
|
25
25
|
"check:python": "node scripts/run_python.mjs scripts/check_python.py",
|
|
26
|
-
"lint": "node --check tests/visual/mock_server.cjs && node --check tests/visual/v3.spec.js && npm run lint:frontend && node scripts/check_i18n_literals.mjs",
|
|
26
|
+
"lint": "npm run lint:python && node --check tests/visual/mock_server.cjs && node --check tests/visual/v3.spec.js && npm run lint:frontend && node scripts/check_i18n_literals.mjs",
|
|
27
|
+
"lint:python": "node scripts/run_python.mjs -m ruff check .",
|
|
27
28
|
"lint:frontend": "node scripts/lint_frontend.mjs",
|
|
28
29
|
"docs:check-links": "node scripts/check_markdown_links.mjs",
|
|
29
30
|
"typecheck": "npm run typecheck:frontend && cd vscode-extension && npm run build",
|
package/scripts/bump_version.py
CHANGED
|
@@ -27,6 +27,8 @@ TARGETS = [
|
|
|
27
27
|
("latticeai/core/workspace_os.py", "regex", r'(WORKSPACE_OS_VERSION = ")([^"]+)(")'),
|
|
28
28
|
("latticeai/core/marketplace.py", "regex", r'(MARKETPLACE_VERSION = ")([^"]+)(")'),
|
|
29
29
|
("lattice_brain/runtime/multi_agent.py", "regex", r'(MULTI_AGENT_VERSION = ")([^"]+)(")'),
|
|
30
|
+
("latticeai/services/architecture_readiness.py", "regex", r'(ARCHITECTURE_VERSION_TARGET = ")([^"]+)(")'),
|
|
31
|
+
("latticeai/services/product_readiness.py", "regex", r'(PRODUCT_VERSION_TARGET = ")([^"]+)(")'),
|
|
30
32
|
("pyproject.toml", "regex", r'(^version = ")([^"]+)(")'),
|
|
31
33
|
("package.json", "json", "version"),
|
|
32
34
|
("package-lock.json", "package-lock", None),
|