ltcai 11.2.0 → 11.4.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.
Files changed (247) hide show
  1. package/README.md +46 -53
  2. package/docs/CHANGELOG.md +61 -0
  3. package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
  4. package/docs/DEVELOPMENT.md +1 -1
  5. package/docs/MULTI_AGENT_RUNTIME.md +1 -1
  6. package/docs/ONBOARDING.md +1 -1
  7. package/docs/OPERATIONS.md +6 -2
  8. package/docs/PERMISSION_MODE.md +1 -1
  9. package/docs/TRUST_MODEL.md +1 -1
  10. package/docs/WHY_LATTICE.md +1 -1
  11. package/docs/kg-schema.md +2 -2
  12. package/docs/v11.3.0_PLAN.md +202 -0
  13. package/docs/v11.4.0_RUST_FOUNDATION_PLAN.md +176 -0
  14. package/lattice_brain/__init__.py +1 -1
  15. package/lattice_brain/graph/_kg_common/__init__.py +287 -0
  16. package/lattice_brain/graph/_kg_common/extraction.py +516 -0
  17. package/lattice_brain/graph/_kg_common/relations.py +161 -0
  18. package/lattice_brain/graph/_kg_common/text.py +479 -0
  19. package/lattice_brain/graph/discovery_index/__init__.py +35 -0
  20. package/lattice_brain/graph/discovery_index/cleanup.py +182 -0
  21. package/lattice_brain/graph/discovery_index/extract.py +137 -0
  22. package/lattice_brain/graph/discovery_index/scan.py +411 -0
  23. package/lattice_brain/graph/discovery_index/upsert.py +495 -0
  24. package/lattice_brain/graph/projection/__init__.py +42 -0
  25. package/lattice_brain/graph/projection/curation.py +500 -0
  26. package/lattice_brain/graph/{projection.py → projection/v2_schema.py} +15 -477
  27. package/lattice_brain/graph/retrieval/__init__.py +54 -0
  28. package/lattice_brain/graph/retrieval/context.py +197 -0
  29. package/lattice_brain/graph/retrieval/graph_view.py +319 -0
  30. package/lattice_brain/graph/retrieval/hybrid.py +488 -0
  31. package/lattice_brain/graph/retrieval/maintenance.py +121 -0
  32. package/lattice_brain/graph/retrieval/signals.py +95 -0
  33. package/lattice_brain/graph/retrieval_vector/__init__.py +42 -0
  34. package/lattice_brain/graph/retrieval_vector/fingerprint.py +97 -0
  35. package/lattice_brain/graph/retrieval_vector/indexing.py +347 -0
  36. package/lattice_brain/graph/retrieval_vector/search.py +560 -0
  37. package/lattice_brain/graph/retrieval_vector/status.py +374 -0
  38. package/lattice_brain/ingestion/__init__.py +130 -0
  39. package/lattice_brain/ingestion/_contract.py +90 -0
  40. package/lattice_brain/ingestion/constants.py +127 -0
  41. package/lattice_brain/ingestion/folder_scan.py +57 -0
  42. package/lattice_brain/ingestion/folders.py +258 -0
  43. package/lattice_brain/ingestion/hashing.py +26 -0
  44. package/lattice_brain/ingestion/jobs_api.py +107 -0
  45. package/lattice_brain/ingestion/models.py +80 -0
  46. package/lattice_brain/ingestion/pipeline.py +486 -0
  47. package/lattice_brain/ingestion/quality.py +209 -0
  48. package/lattice_brain/ingestion/routing.py +295 -0
  49. package/lattice_brain/multimodal/__init__.py +164 -0
  50. package/lattice_brain/multimodal/audio.py +77 -0
  51. package/lattice_brain/multimodal/common.py +118 -0
  52. package/lattice_brain/multimodal/images.py +498 -0
  53. package/lattice_brain/multimodal/ports.py +169 -0
  54. package/lattice_brain/multimodal/video.py +410 -0
  55. package/lattice_brain/portability/__init__.py +90 -0
  56. package/lattice_brain/portability/_contract.py +42 -0
  57. package/lattice_brain/portability/backups.py +338 -0
  58. package/lattice_brain/portability/bundles.py +136 -0
  59. package/lattice_brain/portability/constants.py +93 -0
  60. package/lattice_brain/portability/fsops.py +138 -0
  61. package/lattice_brain/portability/service.py +41 -0
  62. package/lattice_brain/{portability.py → portability/sharing.py} +44 -677
  63. package/lattice_brain/runtime/__init__.py +1 -1
  64. package/lattice_brain/runtime/multi_agent.py +1 -1
  65. package/latticeai/__init__.py +1 -1
  66. package/latticeai/api/chronicle.py +63 -0
  67. package/latticeai/core/agent/__init__.py +93 -0
  68. package/latticeai/core/agent/_contract.py +79 -0
  69. package/latticeai/core/agent/context.py +57 -0
  70. package/latticeai/core/agent/deps.py +125 -0
  71. package/latticeai/core/agent/execution.py +622 -0
  72. package/latticeai/core/agent/planning.py +145 -0
  73. package/latticeai/core/agent/recovery.py +157 -0
  74. package/latticeai/core/agent/runtime.py +210 -0
  75. package/latticeai/core/agent/verification.py +231 -0
  76. package/latticeai/core/embedding_providers/__init__.py +151 -0
  77. package/latticeai/core/embedding_providers/base.py +199 -0
  78. package/latticeai/core/embedding_providers/captions.py +162 -0
  79. package/latticeai/core/embedding_providers/profiles.py +126 -0
  80. package/latticeai/core/embedding_providers/text.py +350 -0
  81. package/latticeai/core/embedding_providers/vision.py +352 -0
  82. package/latticeai/core/file_generation/__init__.py +115 -0
  83. package/latticeai/core/file_generation/bundles.py +76 -0
  84. package/latticeai/core/file_generation/extraction.py +154 -0
  85. package/latticeai/core/file_generation/inference.py +235 -0
  86. package/latticeai/core/file_generation/orchestration.py +152 -0
  87. package/latticeai/core/file_generation/prompting.py +117 -0
  88. package/latticeai/core/file_generation/repair.py +114 -0
  89. package/latticeai/core/file_generation/sanitize.py +61 -0
  90. package/latticeai/core/file_generation/validation.py +201 -0
  91. package/latticeai/core/legacy_compatibility.py +1 -1
  92. package/latticeai/core/marketplace.py +1 -1
  93. package/latticeai/core/messages.py +9 -0
  94. package/latticeai/core/workspace_os_constants.py +1 -1
  95. package/latticeai/integrations/telegram_bot/__init__.py +123 -0
  96. package/latticeai/integrations/telegram_bot/__main__.py +17 -0
  97. package/latticeai/integrations/telegram_bot/config.py +86 -0
  98. package/latticeai/integrations/telegram_bot/dispatch.py +311 -0
  99. package/latticeai/integrations/telegram_bot/flows.py +478 -0
  100. package/latticeai/integrations/telegram_bot/helpers.py +322 -0
  101. package/latticeai/integrations/telegram_bot/screens.py +394 -0
  102. package/latticeai/models/router/__init__.py +88 -0
  103. package/latticeai/models/router/_contract.py +66 -0
  104. package/latticeai/models/router/branding.py +56 -0
  105. package/latticeai/models/router/catalog.py +69 -0
  106. package/latticeai/models/router/documents.py +199 -0
  107. package/latticeai/models/router/errors.py +37 -0
  108. package/latticeai/models/router/generation.py +258 -0
  109. package/latticeai/models/router/loading.py +291 -0
  110. package/latticeai/models/router/local_models.py +85 -0
  111. package/latticeai/models/router/registry.py +147 -0
  112. package/latticeai/runtime/build_phases/__init__.py +82 -0
  113. package/latticeai/runtime/build_phases/features.py +407 -0
  114. package/latticeai/runtime/build_phases/foundation.py +555 -0
  115. package/latticeai/runtime/build_phases/web.py +492 -0
  116. package/latticeai/runtime/runtime_context.py +1 -0
  117. package/latticeai/services/architecture_readiness.py +48 -19
  118. package/latticeai/services/brain_intelligence/__init__.py +58 -0
  119. package/latticeai/services/brain_intelligence/_contract.py +71 -0
  120. package/latticeai/services/brain_intelligence/consistency.py +193 -0
  121. package/latticeai/services/brain_intelligence/constants.py +47 -0
  122. package/latticeai/services/brain_intelligence/digest.py +258 -0
  123. package/latticeai/services/brain_intelligence/health.py +331 -0
  124. package/latticeai/services/brain_intelligence/proposals.py +264 -0
  125. package/latticeai/services/brain_intelligence/sampling.py +84 -0
  126. package/latticeai/services/brain_intelligence/service.py +48 -0
  127. package/latticeai/services/chronicle.py +557 -0
  128. package/latticeai/services/memory_service/__init__.py +52 -0
  129. package/latticeai/services/memory_service/_contract.py +100 -0
  130. package/latticeai/services/memory_service/brief.py +431 -0
  131. package/latticeai/services/memory_service/constants.py +57 -0
  132. package/latticeai/services/memory_service/maintenance.py +138 -0
  133. package/latticeai/services/memory_service/manager.py +186 -0
  134. package/latticeai/services/memory_service/proof.py +136 -0
  135. package/latticeai/services/memory_service/recall.py +225 -0
  136. package/latticeai/services/memory_service/service.py +48 -0
  137. package/latticeai/services/memory_service/stores.py +110 -0
  138. package/latticeai/services/model_runtime/__init__.py +322 -0
  139. package/latticeai/services/model_runtime/cloud.py +87 -0
  140. package/latticeai/services/model_runtime/download.py +282 -0
  141. package/latticeai/services/model_runtime/engines.py +341 -0
  142. package/latticeai/services/model_runtime/loading.py +178 -0
  143. package/latticeai/services/model_runtime/service.py +129 -0
  144. package/latticeai/services/model_runtime/state.py +131 -0
  145. package/latticeai/services/model_runtime/status.py +255 -0
  146. package/latticeai/services/product_readiness.py +15 -7
  147. package/latticeai/setup/wizard/__init__.py +126 -0
  148. package/latticeai/setup/wizard/catalog.py +172 -0
  149. package/latticeai/setup/wizard/detect.py +323 -0
  150. package/latticeai/setup/wizard/install.py +348 -0
  151. package/latticeai/setup/wizard/paths.py +168 -0
  152. package/latticeai/setup/wizard/plans.py +74 -0
  153. package/latticeai/setup/wizard/recommend.py +320 -0
  154. package/package.json +6 -2
  155. package/scripts/bump_version.py +14 -0
  156. package/scripts/capture_release_evidence.mjs +33 -21
  157. package/scripts/check_current_release_docs.mjs +1 -1
  158. package/scripts/check_i18n_namespace_coverage.mjs +41 -4
  159. package/scripts/check_max_file_lines.mjs +102 -0
  160. package/scripts/check_release_evidence_bound.mjs +30 -15
  161. package/scripts/check_screenshot_pixel_delta.py +34 -4
  162. package/scripts/check_server_i18n.mjs +1 -0
  163. package/scripts/generate_rust_parity_fixtures.py +562 -0
  164. package/scripts/lib/mock_server_fingerprint.mjs +94 -0
  165. package/scripts/release_screen_claims.json +31 -2
  166. package/src-tauri/Cargo.lock +361 -3
  167. package/src-tauri/Cargo.toml +6 -1
  168. package/src-tauri/src/backend.rs +349 -0
  169. package/src-tauri/src/folder.rs +33 -0
  170. package/src-tauri/src/main.rs +97 -399
  171. package/src-tauri/tauri.conf.json +1 -1
  172. package/static/app/asset-manifest.json +41 -37
  173. package/static/app/assets/Act-yYpYnn0v.js +1 -0
  174. package/static/app/assets/AdminConsole-DL3Cr5pL.js +1 -0
  175. package/static/app/assets/{Brain-tuhI4sOC.js → Brain-C1HBN0Wf.js} +2 -2
  176. package/static/app/assets/BrainHome-DoXRhUUC.js +2 -0
  177. package/static/app/assets/BrainSignals-6yR6ir5t.js +1 -0
  178. package/static/app/assets/Capture-CFIRsFNE.js +1 -0
  179. package/static/app/assets/Chronicle-BZbEgiwN.js +1 -0
  180. package/static/app/assets/CommandPalette-D2pMxC2I.js +1 -0
  181. package/static/app/assets/Library-DwO3yZST.js +1 -0
  182. package/static/app/assets/{LivingBrain-DBwhto14.js → LivingBrain-Jn1GK0-S.js} +1 -1
  183. package/static/app/assets/ProductFlow-B-w1R4Oo.js +1 -0
  184. package/static/app/assets/ReviewCard-6B27X8Vg.js +3 -0
  185. package/static/app/assets/System-DW8F-2xL.js +1 -0
  186. package/static/app/assets/arrow-left-DXvKg9U6.js +1 -0
  187. package/static/app/assets/{bot-Cia42c2h.js → bot-IM_E_Y12.js} +1 -1
  188. package/static/app/assets/brain-Ci1CkWjM.js +1 -0
  189. package/static/app/assets/{button-2j2Ijzgq.js → button-COwyqfHM.js} +1 -1
  190. package/static/app/assets/circle-check-DfInj-qD.js +1 -0
  191. package/static/app/assets/{circle-pause-BEFeWpVW.js → circle-pause-DEM4A1Y5.js} +1 -1
  192. package/static/app/assets/{circle-play-ujXMcHxl.js → circle-play-C9djDuLd.js} +1 -1
  193. package/static/app/assets/{cpu-k4awryFq.js → cpu-DFdo1gw-.js} +1 -1
  194. package/static/app/assets/{download-DFbLJ_ig.js → download-SnJL6oqk.js} +1 -1
  195. package/static/app/assets/{folder-open-7y_b6xkM.js → folder-open-CqZeDkjE.js} +1 -1
  196. package/static/app/assets/{hard-drive-Bidh02Kr.js → hard-drive-j1jJXYYf.js} +1 -1
  197. package/static/app/assets/{index-DwDl9-8Y.css → index-BLPb5lmE.css} +1 -1
  198. package/static/app/assets/index-_u5iUHDr.js +10 -0
  199. package/static/app/assets/input-B0lPdRQZ.js +1 -0
  200. package/static/app/assets/link-2-CoFbooHS.js +1 -0
  201. package/static/app/assets/{permissionCopy-Bpb83Hx9.js → permissionCopy-BsyLxtao.js} +1 -1
  202. package/static/app/assets/primitives-DEbN-d6p.js +1 -0
  203. package/static/app/assets/search-BybIWPNd.js +1 -0
  204. package/static/app/assets/{share-2-BH1M-WNi.js → share-2-CVtZ_ewX.js} +1 -1
  205. package/static/app/assets/{shield-alert-BlKdBXcG.js → shield-alert-CBi2GNWM.js} +1 -1
  206. package/static/app/assets/{textarea-CCWbUfFB.js → textarea-DNMpB5ih.js} +1 -1
  207. package/static/app/assets/{useFocusTrap-YdHQ7pJ1.js → useFocusTrap-C83t3GXF.js} +1 -1
  208. package/static/app/assets/useMutation-DtbJDoyz.js +1 -0
  209. package/static/app/assets/{useQuery-CXQiwbVT.js → useQuery-Dcp1OChy.js} +1 -1
  210. package/static/app/assets/utils-BlZr7Pd4.js +4 -0
  211. package/static/app/assets/workspace-jJY4RuAV.js +1 -0
  212. package/static/app/index.html +4 -4
  213. package/static/sw.js +1 -1
  214. package/lattice_brain/graph/_kg_common.py +0 -1331
  215. package/lattice_brain/graph/discovery_index.py +0 -1141
  216. package/lattice_brain/graph/retrieval.py +0 -1120
  217. package/lattice_brain/graph/retrieval_vector.py +0 -1293
  218. package/lattice_brain/ingestion.py +0 -1525
  219. package/lattice_brain/multimodal.py +0 -1258
  220. package/latticeai/core/agent.py +0 -1465
  221. package/latticeai/core/embedding_providers.py +0 -1196
  222. package/latticeai/core/file_generation.py +0 -1047
  223. package/latticeai/integrations/telegram_bot.py +0 -1390
  224. package/latticeai/models/router.py +0 -1007
  225. package/latticeai/runtime/build_phases.py +0 -1450
  226. package/latticeai/services/brain_intelligence.py +0 -1083
  227. package/latticeai/services/memory_service.py +0 -1177
  228. package/latticeai/services/model_runtime.py +0 -1281
  229. package/latticeai/setup/wizard.py +0 -1310
  230. package/static/app/assets/Act-AWf0SAKp.js +0 -1
  231. package/static/app/assets/AdminConsole-D0u8Tiyj.js +0 -1
  232. package/static/app/assets/BrainHome-Ts7G_Ila.js +0 -2
  233. package/static/app/assets/BrainSignals-jMYgQ2Ar.js +0 -1
  234. package/static/app/assets/Capture-CqOSzyPr.js +0 -1
  235. package/static/app/assets/CommandPalette-DC0Bzh-I.js +0 -1
  236. package/static/app/assets/Library-CX-bbhmK.js +0 -1
  237. package/static/app/assets/ProductFlow-BHA2cfKI.js +0 -1
  238. package/static/app/assets/ReviewCard-BUhCKRNM.js +0 -3
  239. package/static/app/assets/System-Bu2t5hn1.js +0 -1
  240. package/static/app/assets/arrow-left-Dzwa5zRb.js +0 -1
  241. package/static/app/assets/brain-DJMoqrwx.js +0 -1
  242. package/static/app/assets/index-BpYkzcVm.js +0 -10
  243. package/static/app/assets/input-DSlJJxRs.js +0 -1
  244. package/static/app/assets/primitives-BCx6TvfG.js +0 -1
  245. package/static/app/assets/search-Cgy8cCFJ.js +0 -1
  246. package/static/app/assets/utils-zqPZJxdx.js +0 -4
  247. package/static/app/assets/workspace-DXTihhfU.js +0 -1
@@ -0,0 +1,131 @@
1
+ """The immutable dependency set one model runtime is configured with.
2
+
3
+ ``ModelRuntimeState`` replaced the process-wide module ``STATE`` object: every
4
+ value the runtime needs — router, mode, paths, download/auth policy, the
5
+ app-owned callables — is passed in explicitly and never mutated afterwards, so
6
+ a second ASGI application in the same interpreter cannot inherit the first
7
+ one's credentials or configuration.
8
+
9
+ Also home to the two consent gates (:func:`_download_block`,
10
+ :func:`_engine_install_block`): presence of a token or a model name never
11
+ authorises network or installer activity on its own.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from dataclasses import dataclass, field, fields
17
+ from pathlib import Path
18
+ from typing import Any, Callable, Optional
19
+
20
+ from latticeai.core.model_compat import (
21
+ SMOKE_PROMPT as _SMOKE_PROMPT,
22
+ )
23
+ from latticeai.core.model_compat import (
24
+ friendly_model_runtime_error as _friendly_model_runtime_error,
25
+ )
26
+ from latticeai.core.model_compat import (
27
+ model_runtime_compatibility as _model_runtime_compatibility,
28
+ )
29
+ from latticeai.services.model_errors import ModelRuntimeError
30
+
31
+ # ``model_loading._get_model_runtime_deps`` imports these private names from
32
+ # this module to preserve the historical model_runtime wiring surface.
33
+ _MODEL_LOADING_COMPAT_EXPORTS = (
34
+ _friendly_model_runtime_error,
35
+ _model_runtime_compatibility,
36
+ _SMOKE_PROMPT,
37
+ )
38
+
39
+
40
+ def _missing_current_user(_request: Any) -> Optional[str]:
41
+ return None
42
+
43
+
44
+ def _missing_user_api_key(_email: Optional[str], _provider: str) -> Optional[str]:
45
+ return None
46
+
47
+
48
+ @dataclass(frozen=True, slots=True)
49
+ class ModelRuntimeState:
50
+ """Immutable application-owned dependencies for one model runtime.
51
+
52
+ Upper-case configuration field names intentionally match the long-standing
53
+ composition-root vocabulary. Unlike the former module ``STATE`` object,
54
+ instances are explicit, immutable, and safe to create more than once in a
55
+ process (for example in isolated tests or multiple ASGI applications).
56
+ """
57
+
58
+ router: Any = None
59
+ APP_MODE: str = "local"
60
+ DEFAULT_HOST: str = "127.0.0.1"
61
+ DEFAULT_PORT: int = 4825
62
+ DATA_DIR: Path = field(default_factory=lambda: Path.home() / ".latticeai")
63
+ BASE_DIR: Path = field(default_factory=Path.cwd)
64
+ ENABLE_TELEGRAM: bool = False
65
+ ENABLE_GRAPH: bool = True
66
+ AUTOLOAD_MODELS: bool = False
67
+ MODEL_IDLE_UNLOAD_SECONDS: int = 0
68
+ ALLOW_MODEL_DOWNLOADS: bool = False
69
+ MODEL_DOWNLOAD_TIMEOUT: int = 300
70
+ ALLOW_LOCAL_MODELS: bool = True
71
+ REQUIRE_AUTH: bool = False
72
+ INVITE_GATE_ENABLED: bool = False
73
+ ALLOW_PLAINTEXT_API_KEYS: bool = False
74
+ CORS_ALLOW_NETWORK: bool = False
75
+ PUBLIC_MODEL: str = "openai:gpt-4o-mini"
76
+ LOCAL_MODEL: str = "mlx-community/gemma-4-12B-it-4bit"
77
+ IS_PUBLIC_MODE: bool = False
78
+ keyring: Any = None
79
+ get_current_user: Callable[[Any], Optional[str]] = _missing_current_user
80
+ get_user_api_key: Callable[[Optional[str], str], Optional[str]] = _missing_user_api_key
81
+
82
+
83
+ def create_model_runtime_state(**deps: Any) -> ModelRuntimeState:
84
+ """Create an immutable runtime dependency set with strict key validation."""
85
+
86
+ known = {item.name for item in fields(ModelRuntimeState)}
87
+ unknown = sorted(set(deps) - known)
88
+ if unknown:
89
+ raise TypeError(f"unknown model runtime dependencies: {', '.join(unknown)}")
90
+ return ModelRuntimeState(**deps)
91
+
92
+ def _download_allowed(
93
+ allow_download: bool = False, *, state: ModelRuntimeState
94
+ ) -> bool:
95
+ autoload = state.AUTOLOAD_MODELS
96
+ configured = state.ALLOW_MODEL_DOWNLOADS
97
+ return bool(allow_download) or bool(configured) or bool(autoload)
98
+
99
+
100
+ def _download_block(provider: str, model_name: str) -> None:
101
+ raise ModelRuntimeError(
102
+ status_code=409,
103
+ detail={
104
+ "status": "unavailable",
105
+ "capability": "model_download",
106
+ "provider": provider,
107
+ "model": model_name,
108
+ "reason": (
109
+ "Model files are not present locally. Lattice AI does not start "
110
+ "outbound model downloads by default, and token/model presence "
111
+ "alone never authorizes network activity."
112
+ ),
113
+ "action": "Use the explicit pull/prepare flow with download consent, or set LATTICEAI_ALLOW_MODEL_DOWNLOADS=true.",
114
+ },
115
+ )
116
+
117
+
118
+ def _engine_install_block(engine: str) -> None:
119
+ raise ModelRuntimeError(
120
+ status_code=409,
121
+ detail={
122
+ "status": "unavailable",
123
+ "capability": "engine_install",
124
+ "engine": engine,
125
+ "reason": (
126
+ "The requested local runtime is not installed. Lattice AI does not "
127
+ "run package-manager or installer commands from Model Load by default."
128
+ ),
129
+ "action": "Install the runtime explicitly from Library/System setup, or enable explicit download/install consent for this request.",
130
+ },
131
+ )
@@ -0,0 +1,255 @@
1
+ """What this machine can run: the engine/model status payload.
2
+
3
+ :func:`engine_status` is the single answer the Library screen renders — every
4
+ engine (local, local-server, cloud), whether it is installed, whether its
5
+ install is even supported here, and which catalogue models are already pulled.
6
+ :func:`runtime_features` is the same idea for the application itself, and
7
+ :func:`install_engine` is the one write this module owns.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import Any, Dict, List, Optional
13
+
14
+ from latticeai.models.router import HF_MODELS_ROOT
15
+ from latticeai.services.model_catalog import (
16
+ ENGINE_INSTALLERS,
17
+ ENGINE_MODEL_CATALOG,
18
+ filter_lower_family_versions,
19
+ )
20
+ from latticeai.services.model_engines import (
21
+ install_engine as _install_engine,
22
+ )
23
+ from latticeai.services.model_runtime.download import hf_model_ready
24
+ from latticeai.services.model_runtime.engines import (
25
+ _safe_engine_install_plan,
26
+ engine_installed,
27
+ engine_support_status,
28
+ get_lmstudio_models,
29
+ get_ollama_pulled_models,
30
+ )
31
+ from latticeai.services.model_runtime.state import ModelRuntimeState
32
+
33
+
34
+ def engine_status(
35
+ *,
36
+ state: ModelRuntimeState,
37
+ cloud_verify_cache: Optional[Dict[str, Dict[str, Any]]] = None,
38
+ ) -> List[Dict]:
39
+ r = state.router
40
+ verify_cache = cloud_verify_cache or {}
41
+ cloud_models = r.detected_cloud_models() if r else []
42
+ cloud_by_provider: Dict[str, List[Dict[str, Any]]] = {}
43
+ for model in cloud_models:
44
+ cloud_by_provider.setdefault(model["provider"], []).append(model)
45
+
46
+ ollama_installed = engine_installed("ollama")
47
+ pulled = get_ollama_pulled_models() if ollama_installed else set()
48
+ ollama_models = []
49
+ for m in ENGINE_MODEL_CATALOG["ollama"]:
50
+ pull_name = m["id"].removeprefix("ollama:")
51
+ ollama_models.append({**m, "pulled": pull_name in pulled})
52
+ ollama_models = filter_lower_family_versions(ollama_models)
53
+
54
+ HF_MODELS_ROOT.mkdir(parents=True, exist_ok=True)
55
+ mlx_models = []
56
+ for m in ENGINE_MODEL_CATALOG.get("local_mlx", []):
57
+ repo_id = m["id"]
58
+ mlx_models.append({**m, "pulled": hf_model_ready(repo_id, "local_mlx")})
59
+ mlx_models = filter_lower_family_versions(mlx_models)
60
+
61
+ vllm_models = []
62
+ for m in ENGINE_MODEL_CATALOG.get("vllm", []):
63
+ repo_id = m["id"].removeprefix("vllm:")
64
+ vllm_models.append({**m, "pulled": hf_model_ready(repo_id, "vllm")})
65
+ vllm_models = filter_lower_family_versions(vllm_models)
66
+
67
+ lmstudio_models = []
68
+ downloaded_lmstudio = get_lmstudio_models()
69
+ downloaded_by_key: Dict[str, Dict[str, Any]] = {}
70
+ for item in downloaded_lmstudio:
71
+ key = str(item.get("key") or "").strip()
72
+ if not key:
73
+ continue
74
+ downloaded_by_key[key] = item
75
+ loaded_instances = item.get("loaded_instances") or []
76
+ lmstudio_models.append({
77
+ "id": f"lmstudio:{key}",
78
+ "name": item.get("display_name") or f"LM Studio · {key}",
79
+ "family": item.get("architecture") or item.get("publisher") or "LM Studio",
80
+ "tag": "loaded-server-model" if loaded_instances else "downloaded",
81
+ "size": item.get("params_string") or item.get("format") or "LM Studio",
82
+ "pullable": True,
83
+ "pulled": True,
84
+ })
85
+
86
+ if not lmstudio_models:
87
+ for m in ENGINE_MODEL_CATALOG.get("lmstudio", []):
88
+ lmstudio_models.append({**m, "pulled": False})
89
+ else:
90
+ known_ids = {item["id"] for item in lmstudio_models}
91
+ for m in ENGINE_MODEL_CATALOG.get("lmstudio", []):
92
+ repo_id = m["id"].removeprefix("lmstudio:")
93
+ if f"lmstudio:{repo_id}" not in known_ids and repo_id not in downloaded_by_key:
94
+ lmstudio_models.append({**m, "pulled": False})
95
+ lmstudio_models = filter_lower_family_versions(lmstudio_models)
96
+
97
+ llamacpp_models = []
98
+ for m in ENGINE_MODEL_CATALOG.get("llamacpp", []):
99
+ repo_id = m["id"].removeprefix("llamacpp:")
100
+ llamacpp_models.append({**m, "pulled": hf_model_ready(repo_id, "llamacpp")})
101
+ llamacpp_models = filter_lower_family_versions(llamacpp_models)
102
+
103
+ local_server_specs: List[Dict[str, Any]] = [
104
+ {
105
+ "id": "vllm",
106
+ "name": "vLLM",
107
+ "description": "vLLM OpenAI 호환 서버(예: http://localhost:8000/v1)에 연결합니다.",
108
+ "requires": "VLLM_BASE_URL",
109
+ "note": engine_support_status("vllm").get("reason"),
110
+ },
111
+ {
112
+ "id": "lmstudio",
113
+ "name": "LM Studio",
114
+ "description": "LM Studio 로컬 OpenAI 호환 서버에 연결합니다.",
115
+ "requires": "LMSTUDIO_BASE_URL",
116
+ "note": (
117
+ "다운로드된 모델은 자동 감지하고, 선택 시 필요하면 다운로드 후 바로 로드합니다."
118
+ if downloaded_lmstudio else
119
+ "LM Studio 설치 후 모델을 선택하면 Local Server 시작, 다운로드, 로드를 자동으로 진행합니다."
120
+ ),
121
+ "server_ready": bool(downloaded_lmstudio),
122
+ },
123
+ {
124
+ "id": "llamacpp",
125
+ "name": "llama.cpp",
126
+ "description": "llama.cpp 서버(OpenAI 호환 /v1)에 연결합니다.",
127
+ "requires": "LLAMACPP_BASE_URL",
128
+ },
129
+ ]
130
+
131
+ engines = [
132
+ {
133
+ "id": "local_mlx",
134
+ "name": "MLX",
135
+ "kind": "local",
136
+ "description": "Apple Silicon GPU에서 MLX-VLM 모델을 직접 실행하고, Gemma 4는 필요 시 MLX-LM 텍스트 경로로 재시도합니다.",
137
+ "installed": engine_installed("local_mlx"),
138
+ "installable": True,
139
+ "install_label": ENGINE_INSTALLERS["local_mlx"]["label"],
140
+ "install_plan": _safe_engine_install_plan("local_mlx", base_dir=state.BASE_DIR),
141
+ "models": mlx_models,
142
+ },
143
+ {
144
+ "id": "ollama",
145
+ "name": "Ollama",
146
+ "kind": "local-server",
147
+ "description": "Ollama 로컬 서버를 OpenAI 호환 엔진처럼 사용합니다.",
148
+ "installed": ollama_installed,
149
+ "installable": True,
150
+ "install_label": ENGINE_INSTALLERS["ollama"]["label"],
151
+ "install_plan": _safe_engine_install_plan("ollama", base_dir=state.BASE_DIR),
152
+ "models": ollama_models,
153
+ },
154
+ ]
155
+ for spec in local_server_specs:
156
+ support = engine_support_status(spec["id"])
157
+ engines.append({
158
+ "id": spec["id"],
159
+ "name": spec["name"],
160
+ "kind": "local-server",
161
+ "description": spec["description"],
162
+ "installed": engine_installed(spec["id"]),
163
+ "supported": support["supported"],
164
+ "support_reason": support["reason"],
165
+ "installable": support["supported"] and spec["id"] in ENGINE_INSTALLERS,
166
+ "install_label": ENGINE_INSTALLERS.get(spec["id"], {}).get("label"),
167
+ "install_plan": (
168
+ _safe_engine_install_plan(spec["id"], base_dir=state.BASE_DIR)
169
+ if spec["id"] in ENGINE_INSTALLERS
170
+ else None
171
+ ),
172
+ "requires": spec["requires"],
173
+ "models": (
174
+ vllm_models if spec["id"] == "vllm"
175
+ else lmstudio_models if spec["id"] == "lmstudio"
176
+ else llamacpp_models if spec["id"] == "llamacpp"
177
+ else ENGINE_MODEL_CATALOG.get(spec["id"], [])
178
+ ),
179
+ "note": spec.get("note") or support["reason"] or f"{spec['requires']} 설정 시 활성화됩니다.",
180
+ "server_ready": spec.get("server_ready"),
181
+ })
182
+ for provider in ["openai", "openrouter", "groq", "together", "xai"]:
183
+ env_key = next((item.get("requires") for item in cloud_by_provider.get(provider, []) if item.get("requires")), None)
184
+ provider_models = []
185
+ for model in cloud_by_provider.get(provider, []):
186
+ cache = verify_cache.get(str(model.get("id") or ""))
187
+ provider_models.append({
188
+ **model,
189
+ "verified": cache.get("ok") if cache else None,
190
+ "verify_reason": cache.get("reason") if cache else None,
191
+ })
192
+ engines.append({
193
+ "id": provider,
194
+ "name": provider.title(),
195
+ "kind": "cloud",
196
+ "description": "OpenAI 호환 Chat Completions API로 cloud LLM을 실행합니다.",
197
+ "installed": engine_installed(provider),
198
+ "installable": True,
199
+ "install_label": ENGINE_INSTALLERS[provider]["label"],
200
+ "install_plan": _safe_engine_install_plan(provider, base_dir=state.BASE_DIR),
201
+ "requires": env_key,
202
+ "models": provider_models,
203
+ })
204
+ return engines
205
+
206
+ def runtime_features(*, state: ModelRuntimeState) -> Dict:
207
+ s = state
208
+ r = s.router
209
+ return {
210
+ "mode": s.APP_MODE,
211
+ "public": s.IS_PUBLIC_MODE,
212
+ "host": s.DEFAULT_HOST,
213
+ "port": s.DEFAULT_PORT,
214
+ "data_dir": str(s.DATA_DIR),
215
+ "telegram_enabled": s.ENABLE_TELEGRAM,
216
+ "graph_enabled": s.ENABLE_GRAPH,
217
+ "autoload_models": s.AUTOLOAD_MODELS,
218
+ "model_idle_unload_seconds": s.MODEL_IDLE_UNLOAD_SECONDS,
219
+ "allow_model_downloads": s.ALLOW_MODEL_DOWNLOADS,
220
+ "model_download_timeout": s.MODEL_DOWNLOAD_TIMEOUT,
221
+ "model_memory_policy": r.model_memory_policy() if r else None,
222
+ "allow_local_models": s.ALLOW_LOCAL_MODELS,
223
+ "security": {
224
+ "host": s.DEFAULT_HOST,
225
+ "require_auth": s.REQUIRE_AUTH,
226
+ "invite_gate_enabled": s.INVITE_GATE_ENABLED,
227
+ "keyring_available": s.keyring is not None,
228
+ "plaintext_api_keys_allowed": s.ALLOW_PLAINTEXT_API_KEYS,
229
+ "cors_allow_network": s.CORS_ALLOW_NETWORK,
230
+ },
231
+ "default_model": s.PUBLIC_MODEL if s.IS_PUBLIC_MODE else s.LOCAL_MODEL,
232
+ "local_only_features": {
233
+ "mlx": s.ALLOW_LOCAL_MODELS and not s.IS_PUBLIC_MODE,
234
+ "telegram_bridge": s.ENABLE_TELEGRAM,
235
+ "desktop_chrome_bridge": not s.IS_PUBLIC_MODE,
236
+ "computer_use_bridge": not s.IS_PUBLIC_MODE,
237
+ },
238
+ "public_features": {
239
+ "web_ui": True,
240
+ "openai_compatible_models": True,
241
+ "persistent_data_dir": str(s.DATA_DIR),
242
+ },
243
+ }
244
+
245
+ def install_engine(
246
+ engine: str,
247
+ confirmation_token: Optional[str] = None,
248
+ *,
249
+ state: ModelRuntimeState,
250
+ ) -> Dict:
251
+ return _install_engine(
252
+ engine,
253
+ confirmation_token=confirmation_token,
254
+ base_dir=state.BASE_DIR,
255
+ )
@@ -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 = "11.2.0"
21
+ PRODUCT_VERSION_TARGET = "11.4.0"
22
22
 
23
23
 
24
24
  @dataclass(frozen=True)
@@ -44,7 +44,10 @@ PRODUCT_GATES: List[ProductGate] = [
44
44
  "frontend/src/features/brain/BrainHome.tsx",
45
45
  "frontend/src/App.tsx::brain-mobile-nav",
46
46
  "latticeai/setup/auto_setup.py",
47
- "latticeai/setup/wizard.py",
47
+ # v11.3.0: the wizard is a package (paths / detect / plans /
48
+ # catalog / recommend / install); the recommender is the piece
49
+ # first-run actually shows.
50
+ "latticeai/setup/wizard/recommend.py",
48
51
  ],
49
52
  ),
50
53
  ProductGate(
@@ -64,8 +67,11 @@ PRODUCT_GATES: List[ProductGate] = [
64
67
  "latticeai/api/chat_intents.py::direct_write_file",
65
68
  # v9.9.2 ArtifactWritePipeline: every write path shares the same
66
69
  # extract → validate → repair guarantee, proven by the FG harness.
67
- "latticeai/core/file_generation.py::sanitize_write_content",
68
- "latticeai/core/agent.py::content_sanitize",
70
+ # v11.3.0: both modules became packages — the write-side door is
71
+ # its own submodule, and the agent's use of it lives in the
72
+ # EXECUTE phase.
73
+ "latticeai/core/file_generation/sanitize.py::sanitize_write_content",
74
+ "latticeai/core/agent/execution.py::content_sanitize",
69
75
  "tests/unit/test_artifact_write_scenarios.py::test_fg06_agent_dispatch_strips_fences_from_write_file_content",
70
76
  "tests/unit/test_chat_telegram_decoupling.py::test_chat_file_creation_intent_writes_real_file",
71
77
  ],
@@ -110,12 +116,14 @@ PRODUCT_GATES: List[ProductGate] = [
110
116
  f"docs/CHANGELOG.md::## [{PRODUCT_VERSION_TARGET}]",
111
117
  "FEATURE_STATUS.md",
112
118
  f"RELEASE_NOTES_v{PRODUCT_VERSION_TARGET}.md",
113
- "latticeai/core/agent.py::SingleAgentRuntime",
119
+ # v11.3.0: the single-agent loop is a package; the composed
120
+ # class and its boundary contract live in the runtime half.
121
+ "latticeai/core/agent/runtime.py::SingleAgentRuntime",
114
122
  "lattice_brain/runtime/agent_runtime.py::class AgentRuntime",
115
123
  "lattice_brain/runtime/contracts.py::runtime-boundary/v1",
116
124
  "lattice_brain/runtime/contracts.py::RuntimeBoundaryProtocol",
117
125
  "lattice_brain/runtime/agent_runtime.py::def boundary",
118
- "latticeai/core/agent.py::def boundary",
126
+ "latticeai/core/agent/runtime.py::def boundary",
119
127
  "latticeai/services/architecture_readiness.py::lattice-architecture-contract/v1",
120
128
  "latticeai/services/tool_dispatch.py::rollback_file",
121
129
  ],
@@ -148,7 +156,7 @@ PRODUCT_GATES: List[ProductGate] = [
148
156
  "scripts/brain_quality_eval.py",
149
157
  "scripts/product_readiness.py",
150
158
  "tests/unit/test_product_readiness.py",
151
- "tests/visual/v3.spec.js::Brain Chat Home",
159
+ "tests/visual/v3.surfaces.spec.js::Brain Chat Home",
152
160
  ".github/workflows/ci.yml::scripts/product_readiness.py",
153
161
  ".github/workflows/release.yml::npm run lint",
154
162
  ],
@@ -0,0 +1,126 @@
1
+ """
2
+ Smart Setup Wizard — Environment Scanner, Recommender & Auto-Installer
3
+ Detects hardware, tools, and API keys; returns tailored recommendations;
4
+ streams SSE installation progress.
5
+
6
+ Formerly the root ``setup.py``; renamed in v4 so it no longer collides with
7
+ the setuptools build entrypoint and actually ships in the wheel
8
+ (``pyproject.toml`` py-modules). Packaging is owned entirely by
9
+ ``pyproject.toml`` — there is deliberately no root ``setup.py``.
10
+
11
+ Split into cohesive submodules in v11.3.0 (no behaviour change): ``paths``
12
+ (PATH repair + binary lookup + the module-level ``COMMON_PATH_DIRS``),
13
+ ``detect`` (hardware/OS/tool probes and ``scan_environment``), ``plans``
14
+ (SSE framing + confirmation-token command plans), ``catalog`` (model tables),
15
+ ``recommend`` (``get_recommendations``) and ``install`` (verify/repair and the
16
+ SSE ``install_stream``). This module re-exports every name the single file
17
+ exposed, so ``latticeai.setup.wizard.X`` keeps working.
18
+
19
+ Stubbing note: rebinding one of these names *here* changes only this module's
20
+ name. The submodule that calls it holds its own reference, so a test standing
21
+ in for a helper patches the submodule that uses it.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ # The single file had no ``__all__``, so its public surface was "every module
27
+ # global" — including the names it imported for its own use. Every re-export
28
+ # below therefore uses the redundant-alias form: it reproduces exactly that
29
+ # surface, and it marks each name as deliberate rather than a leftover import.
30
+ from latticeai.core.quiet import quiet as quiet
31
+ from latticeai.services.process_audit import (
32
+ CommandConfirmationError as CommandConfirmationError,
33
+ )
34
+ from latticeai.services.process_audit import (
35
+ append_process_audit_event as append_process_audit_event,
36
+ )
37
+ from latticeai.services.process_audit import command_plan as command_plan
38
+ from latticeai.services.process_audit import (
39
+ command_plan_for_commands as command_plan_for_commands,
40
+ )
41
+ from latticeai.services.process_audit import (
42
+ require_command_confirmation as require_command_confirmation,
43
+ )
44
+ from latticeai.services.setup_detection import detect_cuda as detect_cuda
45
+ from latticeai.services.setup_detection import detect_tools as detect_tools
46
+ from latticeai.services.setup_detection import (
47
+ detect_wsl_from_text as detect_wsl_from_text,
48
+ )
49
+ from latticeai.setup.wizard.catalog import (
50
+ _BEST_MODEL_TIERS as _BEST_MODEL_TIERS,
51
+ )
52
+ from latticeai.setup.wizard.catalog import (
53
+ _CROSS_PLATFORM_MODEL_CATALOG as _CROSS_PLATFORM_MODEL_CATALOG,
54
+ )
55
+ from latticeai.setup.wizard.catalog import (
56
+ _MODEL_CATALOG as _MODEL_CATALOG,
57
+ )
58
+ from latticeai.setup.wizard.catalog import (
59
+ _VERSIONED_MODEL_PATTERNS as _VERSIONED_MODEL_PATTERNS,
60
+ )
61
+ from latticeai.setup.wizard.catalog import (
62
+ _best_model_for_engine as _best_model_for_engine,
63
+ )
64
+ from latticeai.setup.wizard.catalog import (
65
+ _catalog_row_family_version as _catalog_row_family_version,
66
+ )
67
+ from latticeai.setup.wizard.catalog import (
68
+ _filter_lower_family_versions as _filter_lower_family_versions,
69
+ )
70
+ from latticeai.setup.wizard.catalog import (
71
+ _version_tuple as _version_tuple,
72
+ )
73
+ from latticeai.setup.wizard.detect import _cmd as _cmd
74
+ from latticeai.setup.wizard.detect import _detect_api_keys as _detect_api_keys
75
+ from latticeai.setup.wizard.detect import _detect_chip as _detect_chip
76
+ from latticeai.setup.wizard.detect import _detect_cpu as _detect_cpu
77
+ from latticeai.setup.wizard.detect import _detect_cuda as _detect_cuda
78
+ from latticeai.setup.wizard.detect import (
79
+ _detect_disk_free_gb as _detect_disk_free_gb,
80
+ )
81
+ from latticeai.setup.wizard.detect import _detect_gpu as _detect_gpu
82
+ from latticeai.setup.wizard.detect import _detect_mlx as _detect_mlx
83
+ from latticeai.setup.wizard.detect import _detect_ram_gb as _detect_ram_gb
84
+ from latticeai.setup.wizard.detect import _detect_tools as _detect_tools
85
+ from latticeai.setup.wizard.detect import _detect_wsl as _detect_wsl
86
+ from latticeai.setup.wizard.detect import (
87
+ _parse_windows_video_controllers as _parse_windows_video_controllers,
88
+ )
89
+ from latticeai.setup.wizard.detect import scan_environment as scan_environment
90
+ from latticeai.setup.wizard.install import _brew_install as _brew_install
91
+ from latticeai.setup.wizard.install import _pip_install as _pip_install
92
+ from latticeai.setup.wizard.install import _repair_action as _repair_action
93
+ from latticeai.setup.wizard.install import _verify_action as _verify_action
94
+ from latticeai.setup.wizard.install import _verify_binary as _verify_binary
95
+ from latticeai.setup.wizard.install import _wait_for_binary as _wait_for_binary
96
+ from latticeai.setup.wizard.install import install_stream as install_stream
97
+ from latticeai.setup.wizard.install import open_url as open_url
98
+ from latticeai.setup.wizard.paths import COMMON_PATH_DIRS as COMMON_PATH_DIRS
99
+ from latticeai.setup.wizard.paths import OFFICIAL_DOWNLOADS as OFFICIAL_DOWNLOADS
100
+ from latticeai.setup.wizard.paths import PACKAGE_MODULES as PACKAGE_MODULES
101
+ from latticeai.setup.wizard.paths import (
102
+ WINDOWS_BINARY_CANDIDATES as WINDOWS_BINARY_CANDIDATES,
103
+ )
104
+ from latticeai.setup.wizard.paths import _component_detail as _component_detail
105
+ from latticeai.setup.wizard.paths import _merge_path_dirs as _merge_path_dirs
106
+ from latticeai.setup.wizard.paths import _module_available as _module_available
107
+ from latticeai.setup.wizard.paths import _package_module as _package_module
108
+ from latticeai.setup.wizard.paths import _persist_extra_path as _persist_extra_path
109
+ from latticeai.setup.wizard.paths import _project_env_file as _project_env_file
110
+ from latticeai.setup.wizard.paths import _update_env_file as _update_env_file
111
+ from latticeai.setup.wizard.paths import _which_any as _which_any
112
+ from latticeai.setup.wizard.paths import _which_detail as _which_detail
113
+ from latticeai.setup.wizard.paths import repair_path_for as repair_path_for
114
+ from latticeai.setup.wizard.plans import _action_command_plan as _action_command_plan
115
+ from latticeai.setup.wizard.plans import _action_commands as _action_commands
116
+ from latticeai.setup.wizard.plans import _attach_action_plan as _attach_action_plan
117
+ from latticeai.setup.wizard.plans import (
118
+ _hydrate_install_actions as _hydrate_install_actions,
119
+ )
120
+ from latticeai.setup.wizard.plans import _sse as _sse
121
+ from latticeai.setup.wizard.plans import (
122
+ _verify_action_confirmation as _verify_action_confirmation,
123
+ )
124
+ from latticeai.setup.wizard.recommend import (
125
+ get_recommendations as get_recommendations,
126
+ )