ltcai 5.1.0 → 5.3.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 +143 -159
- package/docs/CHANGELOG.md +72 -2355
- package/docs/DEVELOPMENT.md +99 -0
- package/docs/LEGACY_COMPATIBILITY.md +55 -0
- package/docs/V4_1_VALIDATION_REPORT.md +1 -1
- package/docs/V4_3_PRODUCT_HARDENING_REPORT.md +2 -2
- package/docs/V4_5_1_VALIDATION_REPORT.md +2 -1
- package/docs/WHY_LATTICE.md +4 -3
- package/frontend/src/components/FirstRunGuide.tsx +5 -5
- package/frontend/src/components/ProductFlow.tsx +1 -1
- package/frontend/src/i18n.ts +40 -40
- package/frontend/src/pages/Library.tsx +46 -9
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/archive.py +12 -0
- package/lattice_brain/portability.py +14 -0
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/marketplace.py +2 -2
- package/latticeai/api/models.py +20 -4
- package/latticeai/app_factory.py +4 -78
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/workspace_os.py +18 -4
- package/latticeai/runtime/__init__.py +2 -0
- package/latticeai/runtime/brain_runtime.py +41 -0
- package/latticeai/runtime/config_runtime.py +36 -0
- package/latticeai/runtime/security_runtime.py +27 -0
- package/latticeai/services/model_capability_registry.py +482 -0
- package/latticeai/services/model_catalog.py +99 -96
- package/latticeai/services/model_recommendation.py +12 -1
- package/package.json +2 -2
- package/scripts/verify_hf_model_registry.py +306 -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-CQmHhk8Q.css +2 -0
- package/static/app/assets/{index-DONOJfMn.js → index-sOXTFUQc.js} +2 -2
- package/static/app/assets/index-sOXTFUQc.js.map +1 -0
- package/static/app/index.html +2 -2
- package/static/app/assets/index-DONOJfMn.js.map +0 -1
- package/static/app/assets/index-DuYYT2oh.css +0 -2
|
@@ -17,6 +17,7 @@ import hashlib
|
|
|
17
17
|
import json
|
|
18
18
|
import os
|
|
19
19
|
import shutil
|
|
20
|
+
import sqlite3
|
|
20
21
|
import tempfile
|
|
21
22
|
import zipfile
|
|
22
23
|
from datetime import datetime, timezone
|
|
@@ -72,6 +73,18 @@ def _sqlite_siblings(db_path: Path) -> tuple[Path, Path, Path]:
|
|
|
72
73
|
return (db_path, Path(str(db_path) + "-wal"), Path(str(db_path) + "-shm"))
|
|
73
74
|
|
|
74
75
|
|
|
76
|
+
def _checkpoint_sqlite(db_path: Path) -> None:
|
|
77
|
+
if not db_path.exists():
|
|
78
|
+
return
|
|
79
|
+
try:
|
|
80
|
+
with sqlite3.connect(str(db_path)) as conn:
|
|
81
|
+
conn.execute("PRAGMA wal_checkpoint(FULL)")
|
|
82
|
+
except sqlite3.Error:
|
|
83
|
+
# Best-effort only. Existing sibling backup/restore still preserves
|
|
84
|
+
# the WAL files if a live connection prevents a checkpoint.
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
|
|
75
88
|
def _restore_sibling(path: Path, backup: Path) -> None:
|
|
76
89
|
if backup.exists():
|
|
77
90
|
shutil.copy2(backup, path)
|
|
@@ -85,6 +98,7 @@ def _replace_sqlite_atomically(src: Path, dest: Path, backup_dir: Path) -> None:
|
|
|
85
98
|
shutil.copyfile(src, tmp)
|
|
86
99
|
backups: dict[Path, Path] = {}
|
|
87
100
|
try:
|
|
101
|
+
_checkpoint_sqlite(dest)
|
|
88
102
|
# -wal/-shm are transient: another live connection can checkpoint and
|
|
89
103
|
# remove them between exists() and the copy/unlink. Treat a vanished
|
|
90
104
|
# sibling as "nothing to preserve" instead of crashing the restore.
|
|
@@ -14,7 +14,7 @@ from datetime import datetime
|
|
|
14
14
|
from typing import Any, Callable, Dict, List, Optional
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
MULTI_AGENT_VERSION = "5.
|
|
17
|
+
MULTI_AGENT_VERSION = "5.3.0"
|
|
18
18
|
|
|
19
19
|
AGENT_ROLES = ("researcher", "planner", "executor", "reviewer", "release")
|
|
20
20
|
CORE_PIPELINE = ("planner", "executor", "reviewer")
|
package/latticeai/__init__.py
CHANGED
|
@@ -88,7 +88,7 @@ def create_marketplace_router(
|
|
|
88
88
|
@router.get("/marketplace/templates/registry")
|
|
89
89
|
async def template_registry(request: Request):
|
|
90
90
|
require_user(request)
|
|
91
|
-
gate_read(request)
|
|
92
|
-
return {"registry": store.list_template_registry()}
|
|
91
|
+
scope = gate_read(request)
|
|
92
|
+
return {"registry": store.list_template_registry(workspace_id=scope)}
|
|
93
93
|
|
|
94
94
|
return router
|
package/latticeai/api/models.py
CHANGED
|
@@ -425,6 +425,12 @@ def create_models_router(
|
|
|
425
425
|
loaded_ids=_router.loaded_model_ids,
|
|
426
426
|
current_id=_router.current_model_id,
|
|
427
427
|
)
|
|
428
|
+
# 5.2.0: surface structured registry info (verified status, hf, hardware, strategies) for UX
|
|
429
|
+
try:
|
|
430
|
+
from latticeai.services.model_catalog import get_verified_models
|
|
431
|
+
verified = get_verified_models()
|
|
432
|
+
except Exception:
|
|
433
|
+
verified = []
|
|
428
434
|
return {
|
|
429
435
|
"recommended": recommended,
|
|
430
436
|
"cloud": _router.detected_cloud_models(),
|
|
@@ -433,6 +439,12 @@ def create_models_router(
|
|
|
433
439
|
"current": _router.current_model_id,
|
|
434
440
|
"compat_profiles": _list_compat_profiles(),
|
|
435
441
|
"vision": _vision_capability(_router.current_model_id, engines),
|
|
442
|
+
# 5.2+ transparent model capability registry
|
|
443
|
+
"registry": {
|
|
444
|
+
"version": "5.2.0",
|
|
445
|
+
"verified_count": len(verified),
|
|
446
|
+
"verified": verified[:12], # compact; full via /models/recommendations or future dedicated
|
|
447
|
+
},
|
|
436
448
|
}
|
|
437
449
|
|
|
438
450
|
@router.get("/models/compat-profiles")
|
|
@@ -494,9 +506,8 @@ def create_models_router(
|
|
|
494
506
|
async def model_recommendations(request: Request, engine: str = "local_mlx"):
|
|
495
507
|
"""Hardware-aware tri-state model recommendation for this machine.
|
|
496
508
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
grouped by family. Used by the onboarding and model-picker UIs.
|
|
509
|
+
5.2.0: now includes rich capability fields (hf_repo_id, verification,
|
|
510
|
+
hardware, load_strategy, license, safety_notes) from the structured registry.
|
|
500
511
|
"""
|
|
501
512
|
require_user(request)
|
|
502
513
|
from auto_setup import probe as auto_setup_probe
|
|
@@ -504,6 +515,11 @@ def create_models_router(
|
|
|
504
515
|
|
|
505
516
|
profile = await asyncio.to_thread(lambda: auto_setup_probe().to_json())
|
|
506
517
|
catalog = recommend_catalog(profile, engine=engine)
|
|
507
|
-
|
|
518
|
+
try:
|
|
519
|
+
from latticeai.services.model_catalog import get_verified_models
|
|
520
|
+
reg_meta = {"version": "5.2.0", "verified_total": len(get_verified_models())}
|
|
521
|
+
except Exception:
|
|
522
|
+
reg_meta = {"version": "5.2.0"}
|
|
523
|
+
return {"profile": profile, "recommendations": catalog, "registry": reg_meta}
|
|
508
524
|
|
|
509
525
|
return router
|
package/latticeai/app_factory.py
CHANGED
|
@@ -17,90 +17,16 @@ from __future__ import annotations
|
|
|
17
17
|
import threading
|
|
18
18
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
|
19
19
|
|
|
20
|
+
from latticeai.runtime.brain_runtime import build_brain_runtime
|
|
21
|
+
from latticeai.runtime.config_runtime import build_config_runtime
|
|
22
|
+
from latticeai.runtime.security_runtime import build_security_runtime
|
|
23
|
+
|
|
20
24
|
if TYPE_CHECKING: # imports for annotations only — keep module import light
|
|
21
25
|
from fastapi import FastAPI
|
|
22
26
|
|
|
23
27
|
from latticeai.core.config import Config
|
|
24
28
|
|
|
25
29
|
|
|
26
|
-
def build_config_runtime(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
27
|
-
"""Build the app configuration runtime without importing heavy model code."""
|
|
28
|
-
|
|
29
|
-
from latticeai.core.config import Config
|
|
30
|
-
|
|
31
|
-
cfg = config if config is not None else Config.from_env()
|
|
32
|
-
return {
|
|
33
|
-
"CONFIG": cfg,
|
|
34
|
-
"APP_MODE": cfg.app_mode,
|
|
35
|
-
"IS_PUBLIC_MODE": cfg.is_public,
|
|
36
|
-
"DEFAULT_HOST": cfg.host,
|
|
37
|
-
"DEFAULT_PORT": cfg.port,
|
|
38
|
-
"NETWORK_EXPOSED": cfg.network_exposed,
|
|
39
|
-
"ENABLE_TELEGRAM": cfg.enable_telegram,
|
|
40
|
-
"ENABLE_GRAPH": cfg.enable_graph,
|
|
41
|
-
"AUTOLOAD_MODELS": cfg.autoload_models,
|
|
42
|
-
"MODEL_IDLE_UNLOAD_SECONDS": cfg.model_idle_unload_seconds,
|
|
43
|
-
"ALLOW_LOCAL_MODELS": cfg.allow_local_models,
|
|
44
|
-
"REQUIRE_AUTH": cfg.require_auth,
|
|
45
|
-
"ALLOW_PLAINTEXT_API_KEYS": cfg.allow_plaintext_api_keys,
|
|
46
|
-
"CORS_ALLOW_NETWORK": cfg.cors_allow_network,
|
|
47
|
-
"CORS_EXTRA_ORIGINS": cfg.cors_extra_origins,
|
|
48
|
-
"PUBLIC_MODEL": cfg.public_model,
|
|
49
|
-
"LOCAL_MODEL": cfg.local_model,
|
|
50
|
-
"LOCAL_DRAFT_MODEL": cfg.local_draft_model,
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def build_security_runtime(config: "Config") -> Dict[str, Any]:
|
|
55
|
-
"""Build auth/security-derived runtime settings from the central config."""
|
|
56
|
-
|
|
57
|
-
from latticeai.core.security import configure_trusted_proxies
|
|
58
|
-
|
|
59
|
-
configure_trusted_proxies(config.trusted_proxies)
|
|
60
|
-
return {
|
|
61
|
-
"SSO_DISCOVERY_URL": config.sso_discovery_url,
|
|
62
|
-
"SSO_CLIENT_ID": config.sso_client_id,
|
|
63
|
-
"SSO_CLIENT_SECRET": config.sso_client_secret,
|
|
64
|
-
"SSO_REDIRECT_URI": config.sso_redirect_uri,
|
|
65
|
-
"SSO_PROVIDER_NAME": config.sso_provider_name,
|
|
66
|
-
"RATE_LIMIT_ENABLED": config.rate_limit_enabled,
|
|
67
|
-
"OPEN_REGISTRATION": config.open_registration,
|
|
68
|
-
"INVITE_CODE": config.invite_code,
|
|
69
|
-
"INVITE_GATE_ENABLED": config.invite_gate_enabled,
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def build_brain_runtime(
|
|
74
|
-
*,
|
|
75
|
-
data_dir: Any,
|
|
76
|
-
history_file: Any,
|
|
77
|
-
enable_graph: bool,
|
|
78
|
-
embedder: Any,
|
|
79
|
-
storage_engine: Any,
|
|
80
|
-
) -> Dict[str, Any]:
|
|
81
|
-
"""Construct Brain Core storage/conversation primitives behind one seam."""
|
|
82
|
-
|
|
83
|
-
from lattice_brain import BrainCore, ConversationStore
|
|
84
|
-
|
|
85
|
-
brain_core = BrainCore.from_paths(
|
|
86
|
-
data_dir,
|
|
87
|
-
embedder=embedder.provider,
|
|
88
|
-
storage_engine=storage_engine,
|
|
89
|
-
) if enable_graph else None
|
|
90
|
-
knowledge_graph = brain_core.knowledge if brain_core is not None else None
|
|
91
|
-
conversations = (
|
|
92
|
-
brain_core.conversations
|
|
93
|
-
if brain_core is not None
|
|
94
|
-
else ConversationStore(data_dir / "knowledge_graph.sqlite")
|
|
95
|
-
)
|
|
96
|
-
conversations.import_legacy_json(history_file)
|
|
97
|
-
return {
|
|
98
|
-
"BRAIN_CORE": brain_core,
|
|
99
|
-
"KNOWLEDGE_GRAPH": knowledge_graph,
|
|
100
|
-
"CONVERSATIONS": conversations,
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
30
|
def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
105
31
|
"""The legacy ``server_app`` assembly, moved verbatim into function scope.
|
|
106
32
|
|
|
@@ -19,7 +19,7 @@ from pathlib import Path
|
|
|
19
19
|
from typing import Any, Callable, Dict, Iterable, List, Optional
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
WORKSPACE_OS_VERSION = "5.
|
|
22
|
+
WORKSPACE_OS_VERSION = "5.3.0"
|
|
23
23
|
|
|
24
24
|
# Workspace types separate single-user Personal workspaces from shared
|
|
25
25
|
# Organization workspaces. Both keep the same local-first JSON store; the type
|
|
@@ -2309,8 +2309,22 @@ class WorkspaceOSStore:
|
|
|
2309
2309
|
# Marketplace template registry (v2.1 foundation)
|
|
2310
2310
|
# ------------------------------------------------------------------
|
|
2311
2311
|
|
|
2312
|
-
|
|
2313
|
-
|
|
2312
|
+
@staticmethod
|
|
2313
|
+
def _template_registry_key(kind: str, template_id: str, workspace_id: str) -> str:
|
|
2314
|
+
base = f"{kind}:{template_id}"
|
|
2315
|
+
return base if workspace_id == DEFAULT_WORKSPACE_ID else f"{workspace_id}:{base}"
|
|
2316
|
+
|
|
2317
|
+
def list_template_registry(self, workspace_id: Optional[str] = None) -> Dict[str, Any]:
|
|
2318
|
+
state = self.load_state()
|
|
2319
|
+
registry = dict(state.get("template_registry") or {})
|
|
2320
|
+
if workspace_id is None:
|
|
2321
|
+
return registry
|
|
2322
|
+
scope = self._resolve_scope(workspace_id, state)
|
|
2323
|
+
return {
|
|
2324
|
+
key: value
|
|
2325
|
+
for key, value in registry.items()
|
|
2326
|
+
if self._record_workspace(value) == scope
|
|
2327
|
+
}
|
|
2314
2328
|
|
|
2315
2329
|
def mark_template_installed(
|
|
2316
2330
|
self,
|
|
@@ -2323,7 +2337,7 @@ class WorkspaceOSStore:
|
|
|
2323
2337
|
) -> Dict[str, Any]:
|
|
2324
2338
|
state = self.load_state()
|
|
2325
2339
|
scope = self._resolve_scope(workspace_id, state)
|
|
2326
|
-
key =
|
|
2340
|
+
key = self._template_registry_key(kind, template_id, scope)
|
|
2327
2341
|
entry = state.setdefault("template_registry", {}).setdefault(key, {"id": template_id, "kind": kind})
|
|
2328
2342
|
entry.update({
|
|
2329
2343
|
"id": template_id,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Brain Core runtime assembly for app startup."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def build_brain_runtime(
|
|
9
|
+
*,
|
|
10
|
+
data_dir: Any,
|
|
11
|
+
history_file: Any,
|
|
12
|
+
enable_graph: bool,
|
|
13
|
+
embedder: Any,
|
|
14
|
+
storage_engine: Any,
|
|
15
|
+
) -> Dict[str, Any]:
|
|
16
|
+
"""Construct Brain Core storage/conversation primitives behind one seam."""
|
|
17
|
+
|
|
18
|
+
from lattice_brain import BrainCore, ConversationStore
|
|
19
|
+
|
|
20
|
+
brain_core = (
|
|
21
|
+
BrainCore.from_paths(
|
|
22
|
+
data_dir,
|
|
23
|
+
embedder=embedder.provider,
|
|
24
|
+
storage_engine=storage_engine,
|
|
25
|
+
)
|
|
26
|
+
if enable_graph
|
|
27
|
+
else None
|
|
28
|
+
)
|
|
29
|
+
knowledge_graph = brain_core.knowledge if brain_core is not None else None
|
|
30
|
+
conversations = (
|
|
31
|
+
brain_core.conversations
|
|
32
|
+
if brain_core is not None
|
|
33
|
+
else ConversationStore(data_dir / "knowledge_graph.sqlite")
|
|
34
|
+
)
|
|
35
|
+
conversations.import_legacy_json(history_file)
|
|
36
|
+
return {
|
|
37
|
+
"BRAIN_CORE": brain_core,
|
|
38
|
+
"KNOWLEDGE_GRAPH": knowledge_graph,
|
|
39
|
+
"CONVERSATIONS": conversations,
|
|
40
|
+
}
|
|
41
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Configuration runtime assembly for the FastAPI composition root."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from latticeai.core.config import Config
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def build_config_runtime(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
12
|
+
"""Build app configuration values without importing model/runtime code."""
|
|
13
|
+
|
|
14
|
+
from latticeai.core.config import Config
|
|
15
|
+
|
|
16
|
+
cfg = config if config is not None else Config.from_env()
|
|
17
|
+
return {
|
|
18
|
+
"CONFIG": cfg,
|
|
19
|
+
"APP_MODE": cfg.app_mode,
|
|
20
|
+
"IS_PUBLIC_MODE": cfg.is_public,
|
|
21
|
+
"DEFAULT_HOST": cfg.host,
|
|
22
|
+
"DEFAULT_PORT": cfg.port,
|
|
23
|
+
"NETWORK_EXPOSED": cfg.network_exposed,
|
|
24
|
+
"ENABLE_TELEGRAM": cfg.enable_telegram,
|
|
25
|
+
"ENABLE_GRAPH": cfg.enable_graph,
|
|
26
|
+
"AUTOLOAD_MODELS": cfg.autoload_models,
|
|
27
|
+
"MODEL_IDLE_UNLOAD_SECONDS": cfg.model_idle_unload_seconds,
|
|
28
|
+
"ALLOW_LOCAL_MODELS": cfg.allow_local_models,
|
|
29
|
+
"REQUIRE_AUTH": cfg.require_auth,
|
|
30
|
+
"ALLOW_PLAINTEXT_API_KEYS": cfg.allow_plaintext_api_keys,
|
|
31
|
+
"CORS_ALLOW_NETWORK": cfg.cors_allow_network,
|
|
32
|
+
"CORS_EXTRA_ORIGINS": cfg.cors_extra_origins,
|
|
33
|
+
"PUBLIC_MODEL": cfg.public_model,
|
|
34
|
+
"LOCAL_MODEL": cfg.local_model,
|
|
35
|
+
"LOCAL_DRAFT_MODEL": cfg.local_draft_model,
|
|
36
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Security runtime assembly for app startup."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Dict
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from latticeai.core.config import Config
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def build_security_runtime(config: "Config") -> Dict[str, Any]:
|
|
12
|
+
"""Build auth/security-derived runtime settings from the central config."""
|
|
13
|
+
|
|
14
|
+
from latticeai.core.security import configure_trusted_proxies
|
|
15
|
+
|
|
16
|
+
configure_trusted_proxies(config.trusted_proxies)
|
|
17
|
+
return {
|
|
18
|
+
"SSO_DISCOVERY_URL": config.sso_discovery_url,
|
|
19
|
+
"SSO_CLIENT_ID": config.sso_client_id,
|
|
20
|
+
"SSO_CLIENT_SECRET": config.sso_client_secret,
|
|
21
|
+
"SSO_REDIRECT_URI": config.sso_redirect_uri,
|
|
22
|
+
"SSO_PROVIDER_NAME": config.sso_provider_name,
|
|
23
|
+
"RATE_LIMIT_ENABLED": config.rate_limit_enabled,
|
|
24
|
+
"OPEN_REGISTRATION": config.open_registration,
|
|
25
|
+
"INVITE_CODE": config.invite_code,
|
|
26
|
+
"INVITE_GATE_ENABLED": config.invite_gate_enabled,
|
|
27
|
+
}
|