ltcai 4.3.0 → 4.3.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 +19 -17
- package/bin/ltcai.js +6 -2
- package/docs/CHANGELOG.md +33 -3
- package/docs/V4_DIGITAL_BRAIN_RECOVERY.md +16 -22
- package/frontend/openapi.json +11 -1
- package/frontend/src/App.tsx +15 -1
- package/frontend/src/api/client.ts +19 -1
- package/frontend/src/api/openapi.ts +10 -0
- package/frontend/src/pages/Act.tsx +63 -2
- package/frontend/src/pages/Library.tsx +9 -3
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/archive.py +3 -3
- package/lattice_brain/storage/sqlite.py +15 -2
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/agents.py +3 -1
- package/latticeai/api/models.py +66 -18
- package/latticeai/brain/projection.py +12 -2
- package/latticeai/brain/retrieval.py +10 -0
- package/latticeai/brain/store.py +6 -1
- package/latticeai/core/config.py +3 -1
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/multi_agent.py +1 -1
- package/latticeai/core/product_hardening.py +2 -1
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/services/agent_runtime.py +52 -12
- package/latticeai/services/model_runtime.py +83 -2
- package/ltcai_cli.py +14 -3
- package/package.json +3 -2
- package/requirements.txt +17 -0
- package/src-tauri/Cargo.lock +1 -1
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/src/main.rs +257 -25
- package/src-tauri/tauri.conf.json +20 -1
- package/static/app/asset-manifest.json +3 -3
- package/static/app/assets/{index-RiJTJliG.js → index-BhPuj8rT.js} +45 -45
- package/static/app/assets/index-BhPuj8rT.js.map +1 -0
- package/static/app/index.html +1 -1
- package/static/app/assets/index-RiJTJliG.js.map +0 -1
package/README.md
CHANGED
|
@@ -203,31 +203,31 @@ npm run dev
|
|
|
203
203
|
|
|
204
204
|
## Latest Release
|
|
205
205
|
|
|
206
|
-
### v4.3.
|
|
207
|
-
|
|
208
|
-
- **
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
- **
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
identity, permissions, and opt-in integration state.
|
|
206
|
+
### v4.3.1 RC — End-User Audit Repair
|
|
207
|
+
|
|
208
|
+
- **Desktop startup repair** — Tauri launches a visible app, resolves the
|
|
209
|
+
FastAPI sidecar from installed or bundled runtime paths, reports sidecar
|
|
210
|
+
status, and shuts it down on close.
|
|
211
|
+
- **Clean npm install repair** — npm bootstrap ships the required Python
|
|
212
|
+
requirements file and fails honestly if dependencies cannot be installed.
|
|
213
|
+
- **No default outbound model work** — Model Load refuses implicit downloads or
|
|
214
|
+
runtime installs unless the user gives explicit download/install consent.
|
|
215
|
+
- **Honest Act surfaces** — agent simulation is not recorded as real success,
|
|
216
|
+
and workflow create/import/export/run controls call the existing workflow API.
|
|
217
|
+
- **Storage/portability honesty** — sqlite-vec fallback, Postgres dependency
|
|
218
|
+
status, configured ports, and `.latticebrain` bundle sections match runtime
|
|
219
|
+
behavior.
|
|
221
220
|
- **Release hardening** — exact-version validation covers wheel, sdist, npm tgz,
|
|
222
221
|
VSIX, and Tauri DMG artifacts.
|
|
223
222
|
|
|
224
|
-
See [RELEASE_NOTES_v4.3.
|
|
223
|
+
See [RELEASE_NOTES_v4.3.1.md](RELEASE_NOTES_v4.3.1.md),
|
|
224
|
+
[RELEASE_NOTES_v4.3.0.md](RELEASE_NOTES_v4.3.0.md),
|
|
225
225
|
[docs/kg-schema.md](docs/kg-schema.md),
|
|
226
226
|
[FEATURE_STATUS.md](FEATURE_STATUS.md).
|
|
227
227
|
|
|
228
228
|
## How it works — every source converges into the graph
|
|
229
229
|
|
|
230
|
-
As of v4.3.
|
|
230
|
+
As of v4.3.1, data sources flow through the brain ingestion pipeline into
|
|
231
231
|
the Knowledge Graph — no source bypasses it, none becomes an isolated silo:
|
|
232
232
|
|
|
233
233
|
```text
|
|
@@ -290,6 +290,7 @@ For the deeper design, see [ARCHITECTURE.md](ARCHITECTURE.md) and
|
|
|
290
290
|
### Releases
|
|
291
291
|
|
|
292
292
|
- [RELEASE_NOTES.md](RELEASE_NOTES.md) — current release notes
|
|
293
|
+
- [RELEASE_NOTES_v4.3.1.md](RELEASE_NOTES_v4.3.1.md)
|
|
293
294
|
- [RELEASE_NOTES_v4.3.0.md](RELEASE_NOTES_v4.3.0.md)
|
|
294
295
|
- [RELEASE_NOTES_v4.2.0.md](RELEASE_NOTES_v4.2.0.md)
|
|
295
296
|
- [RELEASE_NOTES_v4.1.0.md](RELEASE_NOTES_v4.1.0.md)
|
|
@@ -305,6 +306,7 @@ For the deeper design, see [ARCHITECTURE.md](ARCHITECTURE.md) and
|
|
|
305
306
|
|
|
306
307
|
| Version | Theme |
|
|
307
308
|
| --- | --- |
|
|
309
|
+
| **4.3.1** | End-User Audit Repair RC — desktop sidecar startup, npm clean install, default-off model downloads, honest agent/workflow unavailable states, configured port reporting, storage/portable archive honesty |
|
|
308
310
|
| **4.3.0** | Portability & Product Hardening RC — portable `.latticebrain` archives, confirmed restore/import, pre-migration backup verification, Tauri sidecar hardening, local-only/default-off integration guards, exact-version DMG validation |
|
|
309
311
|
| **4.2.0** | Brain Core & Storage Rebuild — independent `lattice_brain` package, pluggable storage layer, sqlite-vec/pgvector capability reporting, explicit Postgres migration, consent-gated Docker setup, encrypted `.latticebrain` archives |
|
|
310
312
|
| **4.1.0** | Frontend & Desktop Rebuild RC — React/Vite/OpenAPI desktop SPA, Tauri 2.0 primary shell, graph-first navigation, and legacy static frontend removal |
|
package/bin/ltcai.js
CHANGED
|
@@ -60,9 +60,13 @@ function ensureManagedPython() {
|
|
|
60
60
|
|
|
61
61
|
if (!canImport(managedPython, "fastapi")) {
|
|
62
62
|
const requirements = path.join(root, "requirements.txt");
|
|
63
|
+
if (!fs.existsSync(requirements)) {
|
|
64
|
+
console.error(`[LTCAI] Missing ${requirements}. The npm package is incomplete and cannot start the Python server.`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
63
67
|
console.log("[LTCAI] Installing Python dependencies. This can take a few minutes on first run.");
|
|
64
|
-
if (!runSync(managedPython, ["-m", "pip", "install", "--upgrade", "pip"]))
|
|
65
|
-
if (!runSync(managedPython, ["-m", "pip", "install", "-r", requirements]))
|
|
68
|
+
if (!runSync(managedPython, ["-m", "pip", "install", "--upgrade", "pip"])) process.exit(1);
|
|
69
|
+
if (!runSync(managedPython, ["-m", "pip", "install", "-r", requirements])) process.exit(1);
|
|
66
70
|
}
|
|
67
71
|
|
|
68
72
|
return managedPython;
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [4.3.1] - 2026-06-12
|
|
4
|
+
|
|
5
|
+
> End-user audit repair release candidate for v4.3.0 artifacts.
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Tauri desktop startup now launches a visible shell, resolves the FastAPI
|
|
10
|
+
sidecar through installed or bundled runtime paths, reports backend status,
|
|
11
|
+
writes sidecar logs, and shuts down the child process on close.
|
|
12
|
+
- npm clean install now ships `requirements.txt` and fails honestly when Python
|
|
13
|
+
dependency bootstrap cannot complete.
|
|
14
|
+
- Model Load refuses implicit runtime installation and model downloads by
|
|
15
|
+
default, returning actionable unavailable states instead of hanging or making
|
|
16
|
+
unauthorized outbound requests.
|
|
17
|
+
- Agent runtime API refuses simulation-mode execution when no LLM-backed model
|
|
18
|
+
is loaded, preventing fake success records.
|
|
19
|
+
- Workflow UI exposes real create, import, export, and run paths backed by the
|
|
20
|
+
existing workflow API.
|
|
21
|
+
- Runtime version labels, configured port reporting, SSO default redirect URI,
|
|
22
|
+
Postgres dependency status, sqlite-vec fallback reporting, and `.latticebrain`
|
|
23
|
+
bundle claims now match observed behavior.
|
|
24
|
+
|
|
25
|
+
### Artifacts
|
|
26
|
+
|
|
27
|
+
- `dist/ltcai-4.3.1-py3-none-any.whl`
|
|
28
|
+
- `dist/ltcai-4.3.1.tar.gz`
|
|
29
|
+
- `dist/ltcai-4.3.1.vsix`
|
|
30
|
+
- `ltcai-4.3.1.tgz`
|
|
31
|
+
- `src-tauri/target/release/bundle/dmg/Lattice AI_4.3.1_aarch64.dmg`
|
|
32
|
+
|
|
3
33
|
## [4.3.0] - 2026-06-12
|
|
4
34
|
|
|
5
35
|
> Portability & Product Hardening release candidate. v4.3.0 preserves the
|
|
@@ -10,9 +40,9 @@
|
|
|
10
40
|
### Added
|
|
11
41
|
|
|
12
42
|
- `.latticebrain` archive format v2 with encrypted graph DB, blobs, portable
|
|
13
|
-
JSON state,
|
|
14
|
-
identity metadata, manifest hashes, inspect,
|
|
15
|
-
restore dry-run.
|
|
43
|
+
JSON state, workspace export bundles when present, storage metadata,
|
|
44
|
+
provenance, public device identity metadata, manifest hashes, inspect,
|
|
45
|
+
verify, import, restore, and restore dry-run.
|
|
16
46
|
- FastAPI routes for archive inspect/verify/import, backup health, and admin
|
|
17
47
|
product-hardening status.
|
|
18
48
|
- Product-hardening status for local-only startup, storage mode, backup health,
|
|
@@ -5,28 +5,22 @@
|
|
|
5
5
|
> completed analysis. **Update this file before ending any phase and before any
|
|
6
6
|
> likely session/context/usage limit.**
|
|
7
7
|
>
|
|
8
|
-
> Last updated: 2026-06-12 — v4.3.
|
|
8
|
+
> Last updated: 2026-06-12 — v4.3.1 End-User Audit Repair RC; Remaining Gaps remain empty
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
## 0. RELEASE STATUS (v4.3.
|
|
13
|
-
|
|
14
|
-
**v4.3.
|
|
15
|
-
|
|
16
|
-
Latest implementation milestone:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
before copying data. Tauri exposes backend status/restart/shutdown and starts
|
|
23
|
-
the sidecar with loopback/default-off guards. Admin product hardening status
|
|
24
|
-
reports local-only startup posture, storage, backup health, device identity,
|
|
25
|
-
permissions, and opt-in external integration state.
|
|
26
|
-
The v4.3.0 RC process builds validated artifacts only. It does not tag, create a
|
|
12
|
+
## 0. RELEASE STATUS (v4.3.1 RC)
|
|
13
|
+
|
|
14
|
+
**v4.3.1 repairs the v4.3.0 end-user audit blockers without redesigning the
|
|
15
|
+
Digital Brain frontend, Brain Core, storage, or agent/workflow architecture.**
|
|
16
|
+
Latest implementation milestone: desktop sidecar startup/status/shutdown,
|
|
17
|
+
npm clean install bootstrap, default-off model downloads/engine installs,
|
|
18
|
+
agent simulation refusal, workflow create/import/export/run UI, configured
|
|
19
|
+
port reporting, Postgres dependency status, sqlite-vec fallback honesty, and
|
|
20
|
+
`.latticebrain` bundle-section claims now match observed runtime behavior.
|
|
21
|
+
The v4.3.1 RC process builds validated artifacts only. It does not tag, create a
|
|
27
22
|
GitHub Release, publish to PyPI, npm Registry, VS Code Marketplace, Open VSX, or
|
|
28
23
|
deploy to production targets.
|
|
29
|
-
v4.3.0 validation report: `docs/V4_3_VALIDATION_REPORT.md`.
|
|
30
24
|
Remaining implementation gaps: **none**.
|
|
31
25
|
Owner-only blockers: pptx history rewrite (requires force-push/owner decision)
|
|
32
26
|
and consent-gated production embedder provisioning (silent default download is
|
|
@@ -34,11 +28,11 @@ not permitted).
|
|
|
34
28
|
|
|
35
29
|
## Remaining Gaps
|
|
36
30
|
|
|
37
|
-
None. v4.3.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
31
|
+
None. v4.3.1 preserves the already-empty v4.3.0 gap list and closes the audit
|
|
32
|
+
repair work: desktop sidecar startup is visible/statused, npm clean install is
|
|
33
|
+
packaged, Model Load is local-only by default, agent simulation is refused as a
|
|
34
|
+
product success state, workflows have real create/import/export/run paths, and
|
|
35
|
+
storage/archive status surfaces are honest. Owner-only blockers above are
|
|
42
36
|
intentionally not implementation gaps.
|
|
43
37
|
|
|
44
38
|
## 1. Program Charter (from the user's v4.0.0 directive)
|
package/frontend/openapi.json
CHANGED
|
@@ -1349,6 +1349,11 @@
|
|
|
1349
1349
|
],
|
|
1350
1350
|
"title": "Adapter Path"
|
|
1351
1351
|
},
|
|
1352
|
+
"allow_download": {
|
|
1353
|
+
"default": false,
|
|
1354
|
+
"title": "Allow Download",
|
|
1355
|
+
"type": "boolean"
|
|
1356
|
+
},
|
|
1352
1357
|
"draft_model_id": {
|
|
1353
1358
|
"anyOf": [
|
|
1354
1359
|
{
|
|
@@ -1820,6 +1825,11 @@
|
|
|
1820
1825
|
},
|
|
1821
1826
|
"PrepareModelRequest": {
|
|
1822
1827
|
"properties": {
|
|
1828
|
+
"allow_download": {
|
|
1829
|
+
"default": false,
|
|
1830
|
+
"title": "Allow Download",
|
|
1831
|
+
"type": "boolean"
|
|
1832
|
+
},
|
|
1823
1833
|
"engine": {
|
|
1824
1834
|
"anyOf": [
|
|
1825
1835
|
{
|
|
@@ -3543,7 +3553,7 @@
|
|
|
3543
3553
|
},
|
|
3544
3554
|
"info": {
|
|
3545
3555
|
"title": "Lattice AI Server (local)",
|
|
3546
|
-
"version": "4.3.
|
|
3556
|
+
"version": "4.3.1"
|
|
3547
3557
|
},
|
|
3548
3558
|
"openapi": "3.1.0",
|
|
3549
3559
|
"paths": {
|
package/frontend/src/App.tsx
CHANGED
|
@@ -82,6 +82,12 @@ export default function App() {
|
|
|
82
82
|
const [drawer, setDrawer] = React.useState(false);
|
|
83
83
|
const [palette, setPalette] = React.useState(false);
|
|
84
84
|
const health = useQuery({ queryKey: ["health"], queryFn: latticeApi.health });
|
|
85
|
+
const desktop = useQuery({
|
|
86
|
+
queryKey: ["desktopBackendStatus"],
|
|
87
|
+
queryFn: latticeApi.desktopBackendStatus,
|
|
88
|
+
enabled: Boolean(window.__TAURI_INTERNALS__),
|
|
89
|
+
refetchInterval: 5000,
|
|
90
|
+
});
|
|
85
91
|
const workspace = useQuery({ queryKey: ["workspaceOs"], queryFn: latticeApi.workspaceOs });
|
|
86
92
|
|
|
87
93
|
React.useEffect(() => {
|
|
@@ -98,6 +104,11 @@ export default function App() {
|
|
|
98
104
|
return () => window.removeEventListener("keydown", onKey);
|
|
99
105
|
}, []);
|
|
100
106
|
|
|
107
|
+
const healthData = (health.data?.data || {}) as Record<string, unknown>;
|
|
108
|
+
const appVersion = typeof healthData.version === "string" ? healthData.version : null;
|
|
109
|
+
const desktopData = (desktop.data?.data || {}) as Record<string, unknown>;
|
|
110
|
+
const desktopError = typeof desktopData.last_error === "string" ? desktopData.last_error : desktop.data?.error;
|
|
111
|
+
|
|
101
112
|
const rail = (
|
|
102
113
|
<aside className="flex h-full w-64 shrink-0 flex-col border-r border-border bg-card">
|
|
103
114
|
<div className="flex h-16 items-center gap-3 border-b border-border px-4">
|
|
@@ -134,6 +145,9 @@ export default function App() {
|
|
|
134
145
|
</nav>
|
|
135
146
|
<div className="border-t border-border p-3 text-xs text-muted-foreground">
|
|
136
147
|
<div>Server: {health.data?.ok ? "online" : "unavailable"}</div>
|
|
148
|
+
{window.__TAURI_INTERNALS__ ? (
|
|
149
|
+
<div>Sidecar: {desktopData.running ? "running" : desktopError ? `unavailable (${desktopError})` : "starting"}</div>
|
|
150
|
+
) : null}
|
|
137
151
|
<div>Workspace: {String((workspace.data?.data as Record<string, unknown>)?.active_workspace || "local")}</div>
|
|
138
152
|
</div>
|
|
139
153
|
</aside>
|
|
@@ -154,7 +168,7 @@ export default function App() {
|
|
|
154
168
|
<div className="flex min-w-0 items-center gap-2">
|
|
155
169
|
<Button variant="ghost" size="icon" className="lg:hidden" onClick={() => setDrawer(true)}><Menu className="h-5 w-5" /></Button>
|
|
156
170
|
<div className="min-w-0">
|
|
157
|
-
<div className="truncate text-sm text-muted-foreground">
|
|
171
|
+
<div className="truncate text-sm text-muted-foreground">{appVersion ? `v${appVersion}` : "Version unavailable"}</div>
|
|
158
172
|
<div className="truncate font-medium">{primaryRoutes.find((item) => item.id === route.primary)?.label}</div>
|
|
159
173
|
</div>
|
|
160
174
|
</div>
|
|
@@ -38,6 +38,16 @@ async function tauriBackendOrigin(): Promise<string | null> {
|
|
|
38
38
|
return desktopBase;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
async function tauriInvoke<T>(command: string): Promise<T | null> {
|
|
42
|
+
if (!window.__TAURI_INTERNALS__) return null;
|
|
43
|
+
try {
|
|
44
|
+
const { invoke } = await import("@tauri-apps/api/core");
|
|
45
|
+
return await invoke<T>(command);
|
|
46
|
+
} catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
41
51
|
async function apiBase() {
|
|
42
52
|
const stateBase = useAppStore.getState().apiBase;
|
|
43
53
|
if (stateBase) return stateBase;
|
|
@@ -203,6 +213,11 @@ async function streamChat(body: Record<string, unknown>, handlers: ChatEventHand
|
|
|
203
213
|
|
|
204
214
|
export const latticeApi = {
|
|
205
215
|
raw: get,
|
|
216
|
+
desktopBackendStatus: async (): Promise<ApiResult<Record<string, unknown>>> => {
|
|
217
|
+
const status = await tauriInvoke<Record<string, unknown>>("backend_status");
|
|
218
|
+
if (status) return { ok: true, status: 200, data: status, source: "live" };
|
|
219
|
+
return { ok: false, status: 0, data: {}, source: "unavailable", error: "Desktop backend status is available only inside the Tauri shell." };
|
|
220
|
+
},
|
|
206
221
|
health: () => get("/health", {}),
|
|
207
222
|
workspaceOs: () => get("/workspace/os", { counts: {}, models: {}, workspace_registry: { workspaces: [] } }),
|
|
208
223
|
indexStatus: () => get("/api/index/status", {}),
|
|
@@ -248,7 +263,7 @@ export const latticeApi = {
|
|
|
248
263
|
connectFolder: (path: string) => post("/knowledge-graph/local/index", { path, approved: true, watch_enabled: true, consent: { approved: true, source: "desktop-spa" } }, {}),
|
|
249
264
|
localWatchStop: (source_id: string) => post("/knowledge-graph/local/watch/stop", { source_id }, {}),
|
|
250
265
|
models: () => get("/models", { catalog: [], loaded: [], recommended: [] }),
|
|
251
|
-
loadModel: (model_id: string, engine?: string) => post("/models/load", { model_id, engine: engine || null }, {}),
|
|
266
|
+
loadModel: (model_id: string, engine?: string, allow_download = false) => post("/models/load", { model_id, engine: engine || null, allow_download }, {}),
|
|
252
267
|
unloadModel: (model_id: string) => del(`/models/unload/${encodeURIComponent(model_id)}`, {}),
|
|
253
268
|
embeddingsStatus: () => get("/api/embeddings/status", {}),
|
|
254
269
|
agentRuntime: () => get("/agents/api/runtime/status", { runtime: {}, agents: [], runs: [] }),
|
|
@@ -263,6 +278,9 @@ export const latticeApi = {
|
|
|
263
278
|
workflowDefinitions: () => get("/workflows/api/definitions", { workflows: [] }),
|
|
264
279
|
workflowRuns: () => get("/workflows/api/runs", { runs: [] }),
|
|
265
280
|
workflowTriggers: () => get("/workflows/api/triggers", { armed: [] }),
|
|
281
|
+
createWorkflow: (body: { name: string; nodes: Array<Record<string, unknown>>; metadata?: Record<string, unknown> }) => post("/workflows/api/definitions", body, {}),
|
|
282
|
+
importWorkflow: (data: Record<string, unknown>) => post("/workflows/api/import", { data }, {}),
|
|
283
|
+
exportWorkflow: (id: string) => get(`/workflows/api/export/${encodeURIComponent(id)}`, {}),
|
|
266
284
|
runWorkflow: (id: string) => post(`/workflows/api/definitions/${encodeURIComponent(id)}/run`, {}, {}),
|
|
267
285
|
updateWorkflow: (id: string, body: unknown) => patch(`/workflows/api/definitions/${encodeURIComponent(id)}`, body, {}),
|
|
268
286
|
stopWorkflowRun: (id: string) => post(`/workflows/api/runs/${encodeURIComponent(id)}/stop`, {}, {}),
|
|
@@ -6183,6 +6183,11 @@ export interface components {
|
|
|
6183
6183
|
LoadModelRequest: {
|
|
6184
6184
|
/** Adapter Path */
|
|
6185
6185
|
adapter_path?: string | null;
|
|
6186
|
+
/**
|
|
6187
|
+
* Allow Download
|
|
6188
|
+
* @default false
|
|
6189
|
+
*/
|
|
6190
|
+
allow_download: boolean;
|
|
6186
6191
|
/** Draft Model Id */
|
|
6187
6192
|
draft_model_id?: string | null;
|
|
6188
6193
|
/** Engine */
|
|
@@ -6416,6 +6421,11 @@ export interface components {
|
|
|
6416
6421
|
};
|
|
6417
6422
|
/** PrepareModelRequest */
|
|
6418
6423
|
PrepareModelRequest: {
|
|
6424
|
+
/**
|
|
6425
|
+
* Allow Download
|
|
6426
|
+
* @default false
|
|
6427
|
+
*/
|
|
6428
|
+
allow_download: boolean;
|
|
6419
6429
|
/** Engine */
|
|
6420
6430
|
engine?: string | null;
|
|
6421
6431
|
/** Model */
|
|
@@ -58,6 +58,11 @@ function AgentsPanel() {
|
|
|
58
58
|
mutationFn: () => latticeApi.registerAgent({ name: agentName, type: "custom", capabilities: [] }),
|
|
59
59
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["agentRegistry"] }),
|
|
60
60
|
});
|
|
61
|
+
const runtimeData = (runtime.data?.data || {}) as Record<string, unknown>;
|
|
62
|
+
const runtimeMeta = (runtimeData.runtime || {}) as Record<string, unknown>;
|
|
63
|
+
const runtimeReady = Boolean(runtimeMeta.ready);
|
|
64
|
+
const runtimeReason = String(runtimeMeta.unavailable_reason || "Load an LLM-backed model before running agents.");
|
|
65
|
+
const canRunAgent = Boolean(goal.trim()) && runtimeReady && !run.isPending;
|
|
61
66
|
return (
|
|
62
67
|
<div className="grid gap-4 xl:grid-cols-[0.9fr_1.1fr]">
|
|
63
68
|
<Card>
|
|
@@ -67,7 +72,15 @@ function AgentsPanel() {
|
|
|
67
72
|
</CardHeader>
|
|
68
73
|
<CardContent className="space-y-3">
|
|
69
74
|
<Textarea value={goal} onChange={(e) => setGoal(e.target.value)} placeholder="Describe the objective..." />
|
|
70
|
-
|
|
75
|
+
{!runtimeReady ? <Badge variant="warning">{runtimeReason}</Badge> : null}
|
|
76
|
+
<Button
|
|
77
|
+
className="w-full"
|
|
78
|
+
variant={runtimeReady ? "default" : "outline"}
|
|
79
|
+
disabled={!canRunAgent}
|
|
80
|
+
onClick={() => run.mutate()}
|
|
81
|
+
>
|
|
82
|
+
<Play className="h-4 w-4" /> {runtimeReady ? "Run pipeline" : "Agent execution unavailable"}
|
|
83
|
+
</Button>
|
|
71
84
|
{run.data ? <JsonView value={run.data.data || run.data.error} /> : null}
|
|
72
85
|
</CardContent>
|
|
73
86
|
</Card>
|
|
@@ -162,8 +175,26 @@ function RunList({ runs, kind }: { runs: Array<Record<string, unknown>>; kind: "
|
|
|
162
175
|
}
|
|
163
176
|
|
|
164
177
|
function WorkflowsPanel() {
|
|
178
|
+
const qc = useQueryClient();
|
|
165
179
|
const defs = useQuery({ queryKey: ["workflowDefinitions"], queryFn: latticeApi.workflowDefinitions });
|
|
166
180
|
const triggers = useQuery({ queryKey: ["workflowTriggers"], queryFn: latticeApi.workflowTriggers });
|
|
181
|
+
const [name, setName] = React.useState("Manual workflow");
|
|
182
|
+
const [importText, setImportText] = React.useState("");
|
|
183
|
+
const create = useMutation({
|
|
184
|
+
mutationFn: () => latticeApi.createWorkflow({
|
|
185
|
+
name: name.trim() || "Manual workflow",
|
|
186
|
+
nodes: manualWorkflowNodes(),
|
|
187
|
+
metadata: { created_from: "desktop-act-ui" },
|
|
188
|
+
}),
|
|
189
|
+
onSuccess: () => qc.invalidateQueries({ queryKey: ["workflowDefinitions"] }),
|
|
190
|
+
});
|
|
191
|
+
const importWorkflow = useMutation({
|
|
192
|
+
mutationFn: () => latticeApi.importWorkflow(JSON.parse(importText) as Record<string, unknown>),
|
|
193
|
+
onSuccess: () => {
|
|
194
|
+
setImportText("");
|
|
195
|
+
qc.invalidateQueries({ queryKey: ["workflowDefinitions"] });
|
|
196
|
+
},
|
|
197
|
+
});
|
|
167
198
|
const workflows = asArray<Record<string, unknown>>((defs.data?.data as Record<string, unknown>)?.workflows);
|
|
168
199
|
const nodes: Node[] = workflows.slice(0, 12).map((workflow, index) => ({
|
|
169
200
|
id: String(workflow.id || workflow.workflow_id || index),
|
|
@@ -189,7 +220,17 @@ function WorkflowsPanel() {
|
|
|
189
220
|
</Card>
|
|
190
221
|
<DataPanel title="Definitions" result={defs.data}>
|
|
191
222
|
{() => (
|
|
192
|
-
<div className="space-y-
|
|
223
|
+
<div className="space-y-3">
|
|
224
|
+
<div className="grid gap-2 rounded-md border border-border p-3">
|
|
225
|
+
<div className="flex flex-wrap gap-2">
|
|
226
|
+
<Input value={name} onChange={(event) => setName(event.target.value)} placeholder="Workflow name" />
|
|
227
|
+
<Button disabled={create.isPending} onClick={() => create.mutate()}>Create</Button>
|
|
228
|
+
</div>
|
|
229
|
+
<Textarea value={importText} onChange={(event) => setImportText(event.target.value)} placeholder="Paste exported workflow JSON" />
|
|
230
|
+
<Button variant="outline" disabled={!importText.trim() || importWorkflow.isPending} onClick={() => importWorkflow.mutate()}>Import</Button>
|
|
231
|
+
{create.data ? <JsonView value={create.data.data || create.data.error} /> : null}
|
|
232
|
+
{importWorkflow.data ? <JsonView value={importWorkflow.data.data || importWorkflow.data.error} /> : null}
|
|
233
|
+
</div>
|
|
193
234
|
{workflows.length ? workflows.map((workflow) => {
|
|
194
235
|
const id = String(workflow.id || workflow.workflow_id);
|
|
195
236
|
return (
|
|
@@ -197,6 +238,7 @@ function WorkflowsPanel() {
|
|
|
197
238
|
<div className="font-medium">{String(workflow.name || id)}</div>
|
|
198
239
|
<div className="mt-2 flex gap-2">
|
|
199
240
|
<ActionButton label="Run" action={() => latticeApi.runWorkflow(id)} invalidate={["workflowRuns"]} />
|
|
241
|
+
<ActionButton label="Export" action={() => latticeApi.exportWorkflow(id)} />
|
|
200
242
|
</div>
|
|
201
243
|
</div>
|
|
202
244
|
);
|
|
@@ -211,6 +253,25 @@ function WorkflowsPanel() {
|
|
|
211
253
|
);
|
|
212
254
|
}
|
|
213
255
|
|
|
256
|
+
function manualWorkflowNodes(): Array<Record<string, unknown>> {
|
|
257
|
+
return [
|
|
258
|
+
{
|
|
259
|
+
id: "trigger",
|
|
260
|
+
type: "trigger",
|
|
261
|
+
name: "Manual start",
|
|
262
|
+
config: { trigger: "manual" },
|
|
263
|
+
next: "output",
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
id: "output",
|
|
267
|
+
type: "output",
|
|
268
|
+
name: "Output",
|
|
269
|
+
config: { value: "Workflow completed" },
|
|
270
|
+
next: null,
|
|
271
|
+
},
|
|
272
|
+
];
|
|
273
|
+
}
|
|
274
|
+
|
|
214
275
|
function HooksPanel() {
|
|
215
276
|
const hooks = useQuery({ queryKey: ["hooks"], queryFn: latticeApi.hooks });
|
|
216
277
|
const runs = useQuery({ queryKey: ["hookRuns"], queryFn: latticeApi.hookRuns });
|
|
@@ -40,7 +40,6 @@ export function LibraryPage({ initialTab }: { initialTab?: string }) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
function ModelsPanel() {
|
|
43
|
-
const qc = useQueryClient();
|
|
44
43
|
const models = useQuery({ queryKey: ["models"], queryFn: latticeApi.models });
|
|
45
44
|
const emb = useQuery({ queryKey: ["embeddings"], queryFn: latticeApi.embeddingsStatus });
|
|
46
45
|
const catalog = [
|
|
@@ -55,18 +54,25 @@ function ModelsPanel() {
|
|
|
55
54
|
{(catalog.length ? catalog : asArray<Record<string, unknown>>((data as Record<string, unknown>).loaded)).slice(0, 14).map((model, index) => {
|
|
56
55
|
const id = String(model.id || model.model_id || model.name || index);
|
|
57
56
|
const loaded = asArray<string>((data as Record<string, unknown>).loaded).includes(id) || (data as Record<string, unknown>).current === id || model.state === "loaded";
|
|
57
|
+
const loadId = String(model.recommended_load_id || id);
|
|
58
|
+
const engine = String(model.recommended_engine || model.engine || "");
|
|
59
|
+
const loadAvailable = Boolean(model.load_available) || loaded;
|
|
60
|
+
const loadStatus = String(model.load_status || (loaded ? "loaded" : "unavailable"));
|
|
61
|
+
const unavailableReason = String(model.unavailable_reason || "Unavailable until the backend reports a local model/runtime ready.");
|
|
58
62
|
return (
|
|
59
63
|
<div key={id} className="flex flex-wrap items-center justify-between gap-3 rounded-md border border-border bg-background p-3">
|
|
60
64
|
<div>
|
|
61
65
|
<div className="font-medium">{String(model.name || id)}</div>
|
|
62
66
|
<div className="text-sm text-muted-foreground">{String(model.family || model.engine || model.recommended_engine || "local")}</div>
|
|
67
|
+
{!loaded && !loadAvailable ? <div className="mt-1 text-xs text-muted-foreground">{unavailableReason}</div> : null}
|
|
63
68
|
</div>
|
|
64
69
|
<div className="flex items-center gap-2">
|
|
65
|
-
<Badge variant={loaded ? "success" : "muted"}>{loaded ? "loaded" :
|
|
70
|
+
<Badge variant={loaded ? "success" : loadAvailable ? "muted" : "warning"}>{loaded ? "loaded" : loadStatus}</Badge>
|
|
66
71
|
<ActionButton
|
|
67
72
|
label={loaded ? "Unload" : "Load"}
|
|
68
|
-
action={() => loaded ? latticeApi.unloadModel(
|
|
73
|
+
action={() => loaded ? latticeApi.unloadModel(loadId) : latticeApi.loadModel(loadId, engine, false)}
|
|
69
74
|
invalidate={["models"]}
|
|
75
|
+
disabled={!loaded && !loadAvailable}
|
|
70
76
|
/>
|
|
71
77
|
</div>
|
|
72
78
|
</div>
|
package/lattice_brain/archive.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"""Encrypted .latticebrain archive support.
|
|
2
2
|
|
|
3
3
|
The archive is intentionally self-contained and local-only: the encrypted
|
|
4
|
-
payload holds the SQLite brain, blob store, portable JSON state,
|
|
5
|
-
bundles, and public metadata needed to inspect/verify/
|
|
6
|
-
machine without contacting a service.
|
|
4
|
+
payload holds the SQLite brain, blob store, portable JSON state, workspace
|
|
5
|
+
export bundles when present, and public metadata needed to inspect/verify/
|
|
6
|
+
restore on another machine without contacting a service.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
from __future__ import annotations
|
|
@@ -77,7 +77,12 @@ class SQLiteEngine(StorageEngine):
|
|
|
77
77
|
backup_restore=True,
|
|
78
78
|
migrations=True,
|
|
79
79
|
encrypted_archives=True,
|
|
80
|
-
metadata={
|
|
80
|
+
metadata={
|
|
81
|
+
"db_path": str(self.db_path),
|
|
82
|
+
"sqlite_vec_loaded": False,
|
|
83
|
+
"vector_mode": "fallback",
|
|
84
|
+
"honest_fallback": "sqlite-vec has not been probed yet; vector search uses the real brute-force cosine fallback until sqlite-vec is loaded.",
|
|
85
|
+
},
|
|
81
86
|
)
|
|
82
87
|
# Probe on demand so status is accurate even before the graph opens.
|
|
83
88
|
try:
|
|
@@ -94,7 +99,11 @@ class SQLiteEngine(StorageEngine):
|
|
|
94
99
|
return StorageCapabilities(
|
|
95
100
|
engine=self.name,
|
|
96
101
|
available=True,
|
|
97
|
-
reason=None if self._sqlite_vec_loaded else
|
|
102
|
+
reason=None if self._sqlite_vec_loaded else (
|
|
103
|
+
f"{self._sqlite_vec_reason}; using real brute-force cosine fallback, not sqlite-vec ANN"
|
|
104
|
+
if self._sqlite_vec_reason
|
|
105
|
+
else "sqlite-vec unavailable; using real brute-force cosine fallback, not sqlite-vec ANN"
|
|
106
|
+
),
|
|
98
107
|
vector_backend=vector_backend,
|
|
99
108
|
vector_available=True,
|
|
100
109
|
backup_restore=True,
|
|
@@ -103,6 +112,10 @@ class SQLiteEngine(StorageEngine):
|
|
|
103
112
|
metadata={
|
|
104
113
|
"db_path": str(self.db_path),
|
|
105
114
|
"sqlite_vec_loaded": self._sqlite_vec_loaded,
|
|
115
|
+
"sqlite_vec_ann_available": self._sqlite_vec_loaded,
|
|
116
|
+
"vector_mode": "sqlite-vec" if self._sqlite_vec_loaded else "fallback",
|
|
117
|
+
"degraded": not self._sqlite_vec_loaded,
|
|
118
|
+
"honest_fallback": None if self._sqlite_vec_loaded else "Vector search is available through the deterministic brute-force cosine backend. sqlite-vec ANN is unavailable.",
|
|
106
119
|
},
|
|
107
120
|
)
|
|
108
121
|
|
package/latticeai/__init__.py
CHANGED
package/latticeai/api/agents.py
CHANGED
|
@@ -46,7 +46,7 @@ def create_agents_router(
|
|
|
46
46
|
run_executor: Any = None,
|
|
47
47
|
) -> APIRouter:
|
|
48
48
|
from latticeai.core.multi_agent import AGENT_ROLES, ROLE_AGENT_IDS
|
|
49
|
-
from latticeai.services.agent_runtime import AgentRuntime
|
|
49
|
+
from latticeai.services.agent_runtime import AgentRuntime, AgentRuntimeUnavailable
|
|
50
50
|
|
|
51
51
|
# Single AgentRuntime boundary: the router (and via it, the frontend) talks
|
|
52
52
|
# to this façade instead of reaching into the orchestrator/store directly.
|
|
@@ -186,6 +186,8 @@ def create_agents_router(
|
|
|
186
186
|
)
|
|
187
187
|
except ValueError as exc:
|
|
188
188
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
189
|
+
except AgentRuntimeUnavailable as exc:
|
|
190
|
+
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
|
189
191
|
except PermissionError as exc:
|
|
190
192
|
# A pre_run hook gated this run (e.g. a policy/permission hook).
|
|
191
193
|
raise HTTPException(status_code=403, detail=str(exc)) from exc
|