ltcai 4.3.3 → 4.5.1
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 +53 -20
- package/docs/CHANGELOG.md +122 -0
- package/docs/V4_4_0_EXTRACTION_REPORT.md +239 -0
- package/docs/V4_5_0_GEMMA_RUNTIME_COMPATIBILITY_REPORT.md +49 -0
- package/docs/V4_5_0_GRAPH_UX_REPORT.md +34 -0
- package/docs/V4_5_0_MODEL_RUNTIME_UX_REPORT.md +40 -0
- package/docs/V4_5_0_ONBOARDING_REPORT.md +31 -0
- package/docs/V4_5_0_PRODUCT_EXPERIENCE_RECOVERY_REPORT.md +49 -0
- package/docs/V4_5_0_VALIDATION_REPORT.md +60 -0
- package/docs/V4_5_1_GRAPH_EXPERIENCE_REPORT.md +33 -0
- package/docs/V4_5_1_MODEL_EXPERIENCE_REPORT.md +37 -0
- package/docs/V4_5_1_NAVIGATION_REPORT.md +37 -0
- package/docs/V4_5_1_ONBOARDING_REPORT.md +29 -0
- package/docs/V4_5_1_PRODUCT_REIMAGINING_REPORT.md +61 -0
- package/docs/V4_5_1_RC_ARTIFACTS.md +44 -0
- package/docs/V4_5_1_UX_REPORT.md +45 -0
- package/docs/V4_5_1_VALIDATION_REPORT.md +54 -0
- package/docs/V4_5_1_VISUAL_DESIGN_REPORT.md +30 -0
- package/docs/V4_DIGITAL_BRAIN_RECOVERY.md +16 -16
- package/docs/architecture.md +8 -4
- package/frontend/src/App.tsx +152 -91
- package/frontend/src/api/client.ts +83 -1
- package/frontend/src/components/FirstRunGuide.tsx +99 -0
- package/frontend/src/components/primitives.tsx +131 -25
- package/frontend/src/components/ui/badge.tsx +2 -2
- package/frontend/src/components/ui/button.tsx +7 -7
- package/frontend/src/components/ui/card.tsx +5 -5
- package/frontend/src/components/ui/input.tsx +1 -1
- package/frontend/src/components/ui/textarea.tsx +1 -1
- package/frontend/src/pages/Act.tsx +58 -28
- package/frontend/src/pages/Ask.tsx +51 -19
- package/frontend/src/pages/Brain.tsx +60 -42
- package/frontend/src/pages/Capture.tsx +24 -24
- package/frontend/src/pages/Library.tsx +222 -32
- package/frontend/src/pages/System.tsx +56 -34
- package/frontend/src/routes.ts +15 -13
- package/frontend/src/store/appStore.ts +8 -1
- package/frontend/src/styles.css +666 -36
- package/lattice_brain/__init__.py +38 -23
- package/lattice_brain/_kg_common.py +11 -1
- package/lattice_brain/context.py +212 -2
- package/lattice_brain/conversations.py +234 -1
- package/lattice_brain/discovery.py +11 -1
- package/lattice_brain/documents.py +11 -1
- package/lattice_brain/graph/__init__.py +28 -0
- package/lattice_brain/graph/_kg_common.py +1123 -0
- package/lattice_brain/graph/curator.py +473 -0
- package/lattice_brain/graph/discovery.py +1455 -0
- package/lattice_brain/graph/documents.py +218 -0
- package/lattice_brain/graph/identity.py +175 -0
- package/lattice_brain/graph/ingest.py +644 -0
- package/lattice_brain/graph/network.py +205 -0
- package/lattice_brain/graph/projection.py +571 -0
- package/lattice_brain/graph/provenance.py +401 -0
- package/lattice_brain/graph/retrieval.py +1341 -0
- package/lattice_brain/graph/schema.py +640 -0
- package/lattice_brain/graph/store.py +237 -0
- package/lattice_brain/graph/write_master.py +225 -0
- package/lattice_brain/identity.py +11 -13
- package/lattice_brain/ingest.py +11 -1
- package/lattice_brain/ingestion.py +318 -0
- package/lattice_brain/memory.py +100 -1
- package/lattice_brain/network.py +11 -1
- package/lattice_brain/portability.py +431 -0
- package/lattice_brain/projection.py +11 -1
- package/lattice_brain/provenance.py +11 -1
- package/lattice_brain/retrieval.py +11 -1
- package/lattice_brain/runtime/__init__.py +32 -0
- package/lattice_brain/runtime/agent_runtime.py +569 -0
- package/lattice_brain/runtime/hooks.py +754 -0
- package/lattice_brain/runtime/multi_agent.py +795 -0
- package/lattice_brain/schema.py +11 -1
- package/lattice_brain/store.py +10 -2
- package/lattice_brain/workflow.py +461 -0
- package/lattice_brain/write_master.py +11 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/agents.py +2 -2
- package/latticeai/api/browser.py +1 -1
- package/latticeai/api/chat.py +1 -1
- package/latticeai/api/computer_use.py +1 -1
- package/latticeai/api/hooks.py +2 -2
- package/latticeai/api/mcp.py +1 -1
- package/latticeai/api/models.py +107 -18
- package/latticeai/api/tools.py +1 -1
- package/latticeai/api/workflow_designer.py +2 -2
- package/latticeai/app_factory.py +4 -4
- package/latticeai/brain/__init__.py +24 -6
- package/latticeai/brain/_kg_common.py +11 -1117
- package/latticeai/brain/context.py +12 -208
- package/latticeai/brain/conversations.py +12 -231
- package/latticeai/brain/discovery.py +13 -1451
- package/latticeai/brain/documents.py +13 -214
- package/latticeai/brain/identity.py +11 -169
- package/latticeai/brain/ingest.py +13 -640
- package/latticeai/brain/memory.py +12 -97
- package/latticeai/brain/network.py +12 -200
- package/latticeai/brain/projection.py +13 -567
- package/latticeai/brain/provenance.py +13 -397
- package/latticeai/brain/retrieval.py +13 -1337
- package/latticeai/brain/schema.py +12 -635
- package/latticeai/brain/store.py +13 -233
- package/latticeai/brain/write_master.py +13 -221
- package/latticeai/core/agent.py +1 -1
- package/latticeai/core/agent_registry.py +2 -2
- package/latticeai/core/builtin_hooks.py +2 -2
- package/latticeai/core/graph_curator.py +6 -468
- package/latticeai/core/hooks.py +6 -749
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/model_compat.py +250 -0
- package/latticeai/core/multi_agent.py +6 -790
- package/latticeai/core/workflow_engine.py +6 -456
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/models/router.py +136 -32
- package/latticeai/services/agent_runtime.py +6 -564
- package/latticeai/services/ingestion.py +6 -313
- package/latticeai/services/kg_portability.py +6 -426
- package/latticeai/services/model_catalog.py +2 -2
- package/latticeai/services/model_recommendation.py +8 -1
- package/latticeai/services/model_runtime.py +18 -3
- package/latticeai/services/platform_runtime.py +3 -3
- package/latticeai/services/run_executor.py +1 -1
- package/latticeai/services/upload_service.py +1 -1
- package/p_reinforce.py +1 -1
- package/package.json +1 -1
- package/scripts/build_frontend_assets.mjs +12 -1
- package/scripts/bump_version.py +1 -1
- package/scripts/wheel_smoke.py +7 -0
- 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 +5 -5
- package/static/app/assets/index-3G8qcrIS.js +336 -0
- package/static/app/assets/index-3G8qcrIS.js.map +1 -0
- package/static/app/assets/index-C0wYZp7k.css +2 -0
- package/static/app/index.html +2 -2
- package/static/app/assets/index-CHHal8Zl.css +0 -2
- package/static/app/assets/index-pdzil9ac.js +0 -333
- package/static/app/assets/index-pdzil9ac.js.map +0 -1
|
@@ -12,11 +12,14 @@
|
|
|
12
12
|
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
15
|
+
import importlib.util
|
|
16
|
+
import json
|
|
15
17
|
import logging
|
|
16
18
|
import re
|
|
17
19
|
import threading
|
|
18
20
|
import time
|
|
19
21
|
from dataclasses import dataclass, asdict
|
|
22
|
+
from pathlib import Path
|
|
20
23
|
from typing import Any, Dict, List, Optional, Tuple
|
|
21
24
|
|
|
22
25
|
logger = logging.getLogger(__name__)
|
|
@@ -119,6 +122,251 @@ def get_model_profile(model_id: str, engine: Optional[str] = None) -> Dict[str,
|
|
|
119
122
|
return base
|
|
120
123
|
|
|
121
124
|
|
|
125
|
+
# ── Runtime compatibility checks ─────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
GEMMA4_MLX_UNIFIED_MODULE = "mlx_vlm.models.gemma4_unified"
|
|
128
|
+
GEMMA4_MLX_LM_MODULES = ("mlx_lm.models.gemma4", "mlx_lm.models.gemma4_text")
|
|
129
|
+
GEMMA4_UNIFIED_ID_PATTERN = re.compile(r"gemma[-_/ ]?4[-_/ ]?12b", re.I)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _module_available(module: str) -> bool:
|
|
133
|
+
try:
|
|
134
|
+
return importlib.util.find_spec(module) is not None
|
|
135
|
+
except (ImportError, ModuleNotFoundError, AttributeError, ValueError):
|
|
136
|
+
return False
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _is_gemma4(model_id: str) -> bool:
|
|
140
|
+
raw = str(model_id or "").lower()
|
|
141
|
+
return bool(re.search(r"gemma[-_/ ]?4", raw))
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _hf_model_dir(repo_id: str) -> Path:
|
|
145
|
+
return Path.home() / ".ltcai" / "hf-models" / repo_id.replace("/", "__")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _local_model_type(model_id: str) -> Optional[str]:
|
|
149
|
+
raw = str(model_id or "").strip()
|
|
150
|
+
if "gemma4_unified" in raw.lower():
|
|
151
|
+
return "gemma4_unified"
|
|
152
|
+
candidates = []
|
|
153
|
+
explicit = Path(raw).expanduser()
|
|
154
|
+
if raw and explicit.exists():
|
|
155
|
+
candidates.append(explicit / "config.json")
|
|
156
|
+
candidates.append(_hf_model_dir(raw) / "config.json")
|
|
157
|
+
for config_path in candidates:
|
|
158
|
+
try:
|
|
159
|
+
if config_path.exists():
|
|
160
|
+
data = json.loads(config_path.read_text(encoding="utf-8"))
|
|
161
|
+
model_type = str(data.get("model_type") or "").strip().lower()
|
|
162
|
+
if model_type:
|
|
163
|
+
return model_type
|
|
164
|
+
except Exception:
|
|
165
|
+
logger.debug("failed to read model config %s", config_path, exc_info=True)
|
|
166
|
+
if GEMMA4_UNIFIED_ID_PATTERN.search(raw):
|
|
167
|
+
return "gemma4_unified"
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _gemma4_runtime_candidates(raw_model_id: str) -> List[Dict[str, Any]]:
|
|
172
|
+
mlx_available = _module_available("mlx")
|
|
173
|
+
mlx_vlm_available = mlx_available and _module_available("mlx_vlm")
|
|
174
|
+
mlx_vlm_unified_available = mlx_vlm_available and _module_available(GEMMA4_MLX_UNIFIED_MODULE)
|
|
175
|
+
mlx_lm_available = mlx_available and _module_available("mlx_lm")
|
|
176
|
+
mlx_lm_gemma4_available = mlx_lm_available and any(_module_available(module) for module in GEMMA4_MLX_LM_MODULES)
|
|
177
|
+
return [
|
|
178
|
+
{
|
|
179
|
+
"engine": "local_mlx",
|
|
180
|
+
"runtime": "MLX-VLM",
|
|
181
|
+
"load_id": raw_model_id,
|
|
182
|
+
"available": mlx_vlm_available,
|
|
183
|
+
"supports_gemma4_unified": mlx_vlm_unified_available,
|
|
184
|
+
"role": "v3_primary",
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"engine": "local_mlx",
|
|
188
|
+
"runtime": "MLX-LM",
|
|
189
|
+
"load_id": raw_model_id,
|
|
190
|
+
"available": mlx_lm_gemma4_available,
|
|
191
|
+
"role": "v3_text_fallback",
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"engine": "ollama",
|
|
195
|
+
"runtime": "Ollama GGUF",
|
|
196
|
+
"load_id": "ollama:hf.co/ggml-org/gemma-4-12B-it-GGUF:Q4_K_M",
|
|
197
|
+
"available": None,
|
|
198
|
+
"role": "gguf_local_server",
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"engine": "lmstudio",
|
|
202
|
+
"runtime": "LM Studio GGUF",
|
|
203
|
+
"load_id": "lmstudio:ggml-org/gemma-4-12B-it-GGUF",
|
|
204
|
+
"available": None,
|
|
205
|
+
"role": "gguf_local_server",
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"engine": "llamacpp",
|
|
209
|
+
"runtime": "llama.cpp GGUF",
|
|
210
|
+
"load_id": "llamacpp:ggml-org/gemma-4-12B-it-GGUF",
|
|
211
|
+
"available": None,
|
|
212
|
+
"role": "gguf_local_server",
|
|
213
|
+
},
|
|
214
|
+
]
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def model_runtime_compatibility(model_id: str, engine: Optional[str] = None) -> Dict[str, Any]:
|
|
218
|
+
"""Return a lightweight pre-load runtime compatibility signal.
|
|
219
|
+
|
|
220
|
+
This intentionally checks only known fast failure modes. The loader and
|
|
221
|
+
smoke test remain the final authority, but the UI must not present a model
|
|
222
|
+
as ready when the installed runtime is known to lack its required loader.
|
|
223
|
+
"""
|
|
224
|
+
normalized_engine = (engine or "").strip().lower()
|
|
225
|
+
if normalized_engine in {"", "mlx"}:
|
|
226
|
+
normalized_engine = "local_mlx"
|
|
227
|
+
raw_model_id = str(model_id or "")
|
|
228
|
+
if raw_model_id.startswith(("local_mlx:", "mlx:")):
|
|
229
|
+
raw_model_id = raw_model_id.split(":", 1)[1]
|
|
230
|
+
|
|
231
|
+
payload: Dict[str, Any] = {
|
|
232
|
+
"model_id": raw_model_id,
|
|
233
|
+
"engine": normalized_engine or None,
|
|
234
|
+
"family": detect_model_family(raw_model_id),
|
|
235
|
+
"status": "supported",
|
|
236
|
+
"supported": True,
|
|
237
|
+
"checked": True,
|
|
238
|
+
"runtime": None,
|
|
239
|
+
"preferred_runtime": None,
|
|
240
|
+
"runtime_candidates": [],
|
|
241
|
+
"missing_components": [],
|
|
242
|
+
"user_message": None,
|
|
243
|
+
"recovery_guidance": [],
|
|
244
|
+
"alternatives": [],
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if normalized_engine != "local_mlx" or not _is_gemma4(raw_model_id):
|
|
248
|
+
return payload
|
|
249
|
+
|
|
250
|
+
candidates = _gemma4_runtime_candidates(raw_model_id)
|
|
251
|
+
payload["runtime_candidates"] = candidates
|
|
252
|
+
payload["runtime"] = "MLX-VLM"
|
|
253
|
+
payload["preferred_runtime"] = "MLX-VLM"
|
|
254
|
+
model_type = _local_model_type(raw_model_id)
|
|
255
|
+
if model_type:
|
|
256
|
+
payload["model_type"] = model_type
|
|
257
|
+
|
|
258
|
+
mlx_available = _module_available("mlx")
|
|
259
|
+
mlx_vlm_available = _module_available("mlx_vlm")
|
|
260
|
+
mlx_lm_available = any(bool(candidate.get("available")) for candidate in candidates if candidate.get("runtime") == "MLX-LM")
|
|
261
|
+
|
|
262
|
+
if not mlx_available or not (mlx_vlm_available or mlx_lm_available):
|
|
263
|
+
payload.update({
|
|
264
|
+
"status": "runtime_not_installed",
|
|
265
|
+
"checked": False,
|
|
266
|
+
"supported": True,
|
|
267
|
+
"user_message": (
|
|
268
|
+
"Install the local MLX runtime before loading Gemma 4, or choose "
|
|
269
|
+
"the Gemma 4 GGUF route through Ollama, LM Studio, or llama.cpp."
|
|
270
|
+
),
|
|
271
|
+
"alternatives": candidates[2:],
|
|
272
|
+
})
|
|
273
|
+
return payload
|
|
274
|
+
|
|
275
|
+
if model_type == "gemma4_unified" and not _module_available(GEMMA4_MLX_UNIFIED_MODULE):
|
|
276
|
+
payload.update({
|
|
277
|
+
"status": "runtime_update_needed",
|
|
278
|
+
"supported": False,
|
|
279
|
+
"reason_code": "mlx_vlm_missing_gemma4_unified_model",
|
|
280
|
+
"model_type": "gemma4_unified",
|
|
281
|
+
"missing_components": [GEMMA4_MLX_UNIFIED_MODULE],
|
|
282
|
+
"user_message": (
|
|
283
|
+
"Gemma 4 12B uses the gemma4_unified MLX format. The installed "
|
|
284
|
+
"MLX-VLM runtime does not include that loader, so this local "
|
|
285
|
+
"model cannot load until MLX-VLM is updated."
|
|
286
|
+
),
|
|
287
|
+
"recovery_guidance": [
|
|
288
|
+
"Update the MLX runtime from Library/System setup, or run: pip install --upgrade 'mlx-vlm>=0.6.3'.",
|
|
289
|
+
"After the runtime update, re-open Models so Lattice can re-check this model.",
|
|
290
|
+
"Use Gemma 4 26B A4B locally or Gemma 4 12B GGUF through Ollama, LM Studio, or llama.cpp until then.",
|
|
291
|
+
],
|
|
292
|
+
"alternatives": [
|
|
293
|
+
{"id": "mlx-community/gemma-4-26b-a4b-it-4bit", "name": "Gemma 4 26B A4B", "engine": "local_mlx"},
|
|
294
|
+
{"id": "ollama:hf.co/ggml-org/gemma-4-12B-it-GGUF:Q4_K_M", "name": "Gemma 4 12B GGUF", "engine": "ollama"},
|
|
295
|
+
{"id": "lmstudio:ggml-org/gemma-4-12B-it-GGUF", "name": "Gemma 4 12B GGUF", "engine": "lmstudio"},
|
|
296
|
+
],
|
|
297
|
+
"action": "Runtime update needed",
|
|
298
|
+
})
|
|
299
|
+
return payload
|
|
300
|
+
|
|
301
|
+
if mlx_vlm_available:
|
|
302
|
+
return payload
|
|
303
|
+
|
|
304
|
+
payload.update({
|
|
305
|
+
"status": "fallback_available",
|
|
306
|
+
"supported": True,
|
|
307
|
+
"reason_code": "mlx_vlm_missing_gemma4_standard_runtime",
|
|
308
|
+
"missing_components": ["mlx_vlm"],
|
|
309
|
+
"user_message": (
|
|
310
|
+
"MLX-VLM is not available for this Gemma 4 model. Lattice can use "
|
|
311
|
+
"the MLX-LM text fallback or a Gemma 4 GGUF local runtime."
|
|
312
|
+
),
|
|
313
|
+
"recovery_guidance": [
|
|
314
|
+
"Use the MLX-LM text fallback for text chat.",
|
|
315
|
+
"Use a Gemma 4 GGUF model through Ollama, LM Studio, or llama.cpp if the MLX route fails.",
|
|
316
|
+
],
|
|
317
|
+
"alternatives": [
|
|
318
|
+
{"id": candidate["load_id"], "name": candidate["runtime"], "engine": candidate["engine"]}
|
|
319
|
+
for candidate in candidates
|
|
320
|
+
if candidate.get("role") != "v3_primary"
|
|
321
|
+
],
|
|
322
|
+
})
|
|
323
|
+
if mlx_lm_available:
|
|
324
|
+
payload["preferred_runtime"] = "MLX-LM fallback"
|
|
325
|
+
return payload
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def friendly_model_runtime_error(
|
|
329
|
+
error: BaseException | str,
|
|
330
|
+
*,
|
|
331
|
+
model_id: Optional[str] = None,
|
|
332
|
+
engine: Optional[str] = None,
|
|
333
|
+
) -> Dict[str, Any]:
|
|
334
|
+
"""Convert loader exceptions into end-user recoverable error payloads."""
|
|
335
|
+
raw = str(error or "")
|
|
336
|
+
compat = model_runtime_compatibility(model_id or raw, engine=engine)
|
|
337
|
+
if not compat.get("supported", True):
|
|
338
|
+
return {
|
|
339
|
+
"status": compat.get("status") or "unsupported",
|
|
340
|
+
"model_id": model_id,
|
|
341
|
+
"engine": engine,
|
|
342
|
+
"user_message": compat.get("user_message") or (
|
|
343
|
+
"The selected model is not supported by the installed local runtime."
|
|
344
|
+
),
|
|
345
|
+
"recovery_guidance": compat.get("recovery_guidance") or [
|
|
346
|
+
"Choose a recommended alternative model.",
|
|
347
|
+
"Update the local runtime and try validation again.",
|
|
348
|
+
],
|
|
349
|
+
"alternatives": compat.get("alternatives") or [],
|
|
350
|
+
"missing_components": compat.get("missing_components") or [],
|
|
351
|
+
"action": compat.get("action"),
|
|
352
|
+
"reason_code": compat.get("reason_code") or "runtime_model_type_unsupported",
|
|
353
|
+
}
|
|
354
|
+
return {
|
|
355
|
+
"status": "load_failed",
|
|
356
|
+
"model_id": model_id,
|
|
357
|
+
"engine": engine,
|
|
358
|
+
"user_message": (
|
|
359
|
+
"The model could not be loaded. Check that the runtime is installed, "
|
|
360
|
+
"the model files are present, and try a recommended alternative if the issue continues."
|
|
361
|
+
),
|
|
362
|
+
"recovery_guidance": [
|
|
363
|
+
"Open Models and run the setup flow again.",
|
|
364
|
+
"Confirm downloads were explicitly allowed for models that are not on this computer.",
|
|
365
|
+
"Try a recommended smaller local model if memory is low.",
|
|
366
|
+
],
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
|
|
122
370
|
# ── Postprocessing ────────────────────────────────────────────────────────────
|
|
123
371
|
|
|
124
372
|
BAD_MARKERS = [
|
|
@@ -378,7 +626,9 @@ __all__ = [
|
|
|
378
626
|
"FAMILY_PROFILES",
|
|
379
627
|
"CompatProfile",
|
|
380
628
|
"detect_model_family",
|
|
629
|
+
"friendly_model_runtime_error",
|
|
381
630
|
"get_model_profile",
|
|
631
|
+
"model_runtime_compatibility",
|
|
382
632
|
"fast_postprocess",
|
|
383
633
|
"validate_smoke_response",
|
|
384
634
|
"classify_smoke_response",
|