ltcai 6.5.0 → 6.6.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 +22 -20
- package/docs/CHANGELOG.md +34 -0
- package/frontend/openapi.json +65 -1
- package/frontend/src/api/client.ts +1 -0
- package/frontend/src/api/openapi.ts +86 -0
- package/frontend/src/features/brain/BrainComposer.tsx +20 -1
- package/frontend/src/features/brain/BrainConversation.tsx +37 -2
- package/frontend/src/features/brain/BrainHome.tsx +41 -1
- package/frontend/src/features/brain/BrainOverviewPanel.tsx +62 -1
- package/frontend/src/features/brain/brainData.ts +54 -1
- package/frontend/src/features/brain/types.ts +37 -0
- package/frontend/src/i18n.ts +44 -0
- package/frontend/src/styles.css +133 -0
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/graph/ingest.py +41 -6
- package/lattice_brain/ingestion.py +1 -0
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/knowledge_graph.py +11 -0
- package/latticeai/api/memory.py +14 -0
- package/latticeai/app_factory.py +2 -0
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/runtime/chat_wiring.py +2 -0
- package/latticeai/runtime/router_registration.py +3 -0
- package/latticeai/services/memory_service.py +122 -2
- package/latticeai/services/router_context.py +1 -0
- package/latticeai/services/upload_service.py +12 -0
- package/package.json +1 -1
- 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-CdCVz_i4.css → index-C8toxDpv.css} +1 -1
- package/static/app/assets/index-CbvcAQ6B.js +16 -0
- package/static/app/assets/index-CbvcAQ6B.js.map +1 -0
- package/static/app/index.html +2 -2
- package/static/app/assets/index-C1J_1441.js +0 -16
- package/static/app/assets/index-C1J_1441.js.map +0 -1
package/README.md
CHANGED
|
@@ -200,31 +200,32 @@ See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for developer workflow details.
|
|
|
200
200
|
|
|
201
201
|
## Current Release Preparation
|
|
202
202
|
|
|
203
|
-
The current development target is **6.
|
|
204
|
-
|
|
205
|
-
- Brain Home now
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
203
|
+
The current development target is **6.6.0 Brain Proof Runtime**:
|
|
204
|
+
|
|
205
|
+
- Brain Home now shows backend-owned proof that the Brain is a durable context
|
|
206
|
+
store: saved context count, active-model continuity, graph/vector depth, and
|
|
207
|
+
the most recent recallable item.
|
|
208
|
+
- Brain Home now gives first-time users a direct document upload CTA in the
|
|
209
|
+
empty state and composer, backed by workspace-scoped document ingestion.
|
|
210
|
+
- `/api/memory/brain-proof` combines Memory Manager readiness, unified recall,
|
|
211
|
+
graph/vector state, and the active model id so the first screen can say
|
|
212
|
+
"model changed, context stayed" without guessing in the frontend.
|
|
213
|
+
- App-factory interaction wiring now passes active-model state into the Memory
|
|
214
|
+
router through typed runtime context, keeping Brain proof assembly out of the
|
|
215
|
+
monolithic factory body.
|
|
216
|
+
- The release keeps the 6.4.0/6.5.0 Brain readiness and quality hardening intact
|
|
217
|
+
while making the first-five-minute value proposition visible.
|
|
217
218
|
- The release remains local-first: no automatic web/email/calendar ingestion,
|
|
218
219
|
no package publishing, no production deployment, and no external reranker or
|
|
219
220
|
embedding API use without explicit opt-in.
|
|
220
221
|
|
|
221
|
-
Expected artifacts for 6.
|
|
222
|
+
Expected artifacts for 6.6.0 release must use exact filenames:
|
|
222
223
|
|
|
223
|
-
- `dist/ltcai-6.
|
|
224
|
-
- `dist/ltcai-6.
|
|
225
|
-
- `ltcai-6.
|
|
226
|
-
- `dist/ltcai-6.
|
|
227
|
-
- `src-tauri/target/release/bundle/dmg/Lattice AI_6.
|
|
224
|
+
- `dist/ltcai-6.6.0-py3-none-any.whl`
|
|
225
|
+
- `dist/ltcai-6.6.0.tar.gz`
|
|
226
|
+
- `ltcai-6.6.0.tgz`
|
|
227
|
+
- `dist/ltcai-6.6.0.vsix`
|
|
228
|
+
- `src-tauri/target/release/bundle/dmg/Lattice AI_6.6.0_aarch64.dmg`
|
|
228
229
|
|
|
229
230
|
Do not upload `dist/*`. Package registry publishing remains owner-run.
|
|
230
231
|
|
|
@@ -243,6 +244,7 @@ Do not upload `dist/*`. Package registry publishing remains owner-run.
|
|
|
243
244
|
|
|
244
245
|
| Version | Theme |
|
|
245
246
|
| --- | --- |
|
|
247
|
+
| 6.6.0 | Brain Proof Runtime: backend-owned Brain proof API, model-continuity wiring, first-screen proof that saved context can be recalled across model changes, and direct Brain Home document upload |
|
|
246
248
|
| 6.5.0 | Brain Experience Readiness: Brain readiness signal, depth progress rail, source-aware memory-save feedback, and visual coverage for the first-memory loop |
|
|
247
249
|
| 6.4.0 | Digital Brain Quality Hardening: workspace-scoped graph/search/memory reads and mutations, Brain quality primitives, structured context guardrails, and retrieval benchmark coverage |
|
|
248
250
|
| 6.3.1 | Access Runtime / i18n Follow-up: app-factory access-control extraction, focused access runtime tests, and Capture/Review Center i18n coverage |
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,40 @@
|
|
|
3
3
|
The top entry is the current release-preparation target. Older entries are
|
|
4
4
|
historical and may describe behavior as it existed at that release.
|
|
5
5
|
|
|
6
|
+
## [6.6.0] - 2026-06-18
|
|
7
|
+
|
|
8
|
+
> Brain Proof Runtime. Makes the first-five-minute Brain value visible by
|
|
9
|
+
> showing backend-owned evidence that saved context can be recalled and survives
|
|
10
|
+
> model changes.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `/api/memory/brain-proof`, backed by `MemoryService.brain_proof()`, combining
|
|
14
|
+
Brain readiness, durable memory counts, graph/vector depth, active model id,
|
|
15
|
+
and a unified recall sample.
|
|
16
|
+
- Brain Home proof strip for recallable context, model-continuity, and
|
|
17
|
+
knowledge-store state.
|
|
18
|
+
- Recent recall proof card that surfaces the latest memory/item the Brain can
|
|
19
|
+
bring back after a useful exchange.
|
|
20
|
+
- Brain Home document upload CTA in the empty state and composer, giving the
|
|
21
|
+
first session a direct way to grow the Brain with files.
|
|
22
|
+
- Unit coverage for model-independent Brain proof recall behavior.
|
|
23
|
+
- Honest empty-state Brain proof: model-independent capability remains visible,
|
|
24
|
+
but continuity is only marked proven after durable evidence exists.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- Interaction router runtime context now carries an active-model getter into
|
|
28
|
+
the Memory router, reducing app-factory inline wiring while preserving route
|
|
29
|
+
order.
|
|
30
|
+
- Default Brain proof recall seeding is scoped to the current user/workspace
|
|
31
|
+
before falling back to conversation memory.
|
|
32
|
+
- Chat-to-Knowledge-Graph ingestion now carries workspace scope into projected
|
|
33
|
+
graph nodes, and Brain proof conversation counts use scoped conversations so
|
|
34
|
+
another user/workspace cannot make an empty Brain look proven.
|
|
35
|
+
- Direct Knowledge Graph ingest and document upload ingestion now resolve the
|
|
36
|
+
request workspace and project that scope into graph/source/document nodes.
|
|
37
|
+
- Package/runtime/static metadata is synchronized to 6.6.0; package publish and
|
|
38
|
+
deployment remain owner-run only.
|
|
39
|
+
|
|
6
40
|
## [6.5.0] - 2026-06-18
|
|
7
41
|
|
|
8
42
|
> Brain Experience Readiness. Uses the 6.4.0 quality baseline to improve what
|
package/frontend/openapi.json
CHANGED
|
@@ -3757,7 +3757,7 @@
|
|
|
3757
3757
|
},
|
|
3758
3758
|
"info": {
|
|
3759
3759
|
"title": "Lattice AI Server (local)",
|
|
3760
|
-
"version": "
|
|
3760
|
+
"version": "6.6.0"
|
|
3761
3761
|
},
|
|
3762
3762
|
"openapi": "3.1.0",
|
|
3763
3763
|
"paths": {
|
|
@@ -6941,6 +6941,70 @@
|
|
|
6941
6941
|
"summary": "Local Agent Status"
|
|
6942
6942
|
}
|
|
6943
6943
|
},
|
|
6944
|
+
"/api/memory/brain-proof": {
|
|
6945
|
+
"get": {
|
|
6946
|
+
"operationId": "brain_proof_api_memory_brain_proof_get",
|
|
6947
|
+
"parameters": [
|
|
6948
|
+
{
|
|
6949
|
+
"in": "query",
|
|
6950
|
+
"name": "q",
|
|
6951
|
+
"required": false,
|
|
6952
|
+
"schema": {
|
|
6953
|
+
"default": "",
|
|
6954
|
+
"title": "Q",
|
|
6955
|
+
"type": "string"
|
|
6956
|
+
}
|
|
6957
|
+
},
|
|
6958
|
+
{
|
|
6959
|
+
"in": "query",
|
|
6960
|
+
"name": "limit",
|
|
6961
|
+
"required": false,
|
|
6962
|
+
"schema": {
|
|
6963
|
+
"default": 3,
|
|
6964
|
+
"title": "Limit",
|
|
6965
|
+
"type": "integer"
|
|
6966
|
+
}
|
|
6967
|
+
}
|
|
6968
|
+
],
|
|
6969
|
+
"responses": {
|
|
6970
|
+
"200": {
|
|
6971
|
+
"content": {
|
|
6972
|
+
"application/json": {
|
|
6973
|
+
"schema": {}
|
|
6974
|
+
}
|
|
6975
|
+
},
|
|
6976
|
+
"description": "Successful Response"
|
|
6977
|
+
},
|
|
6978
|
+
"422": {
|
|
6979
|
+
"content": {
|
|
6980
|
+
"application/json": {
|
|
6981
|
+
"schema": {
|
|
6982
|
+
"$ref": "#/components/schemas/HTTPValidationError"
|
|
6983
|
+
}
|
|
6984
|
+
}
|
|
6985
|
+
},
|
|
6986
|
+
"description": "Validation Error"
|
|
6987
|
+
}
|
|
6988
|
+
},
|
|
6989
|
+
"summary": "Brain Proof"
|
|
6990
|
+
}
|
|
6991
|
+
},
|
|
6992
|
+
"/api/memory/brain-quality": {
|
|
6993
|
+
"get": {
|
|
6994
|
+
"operationId": "brain_quality_summary_api_memory_brain_quality_get",
|
|
6995
|
+
"responses": {
|
|
6996
|
+
"200": {
|
|
6997
|
+
"content": {
|
|
6998
|
+
"application/json": {
|
|
6999
|
+
"schema": {}
|
|
7000
|
+
}
|
|
7001
|
+
},
|
|
7002
|
+
"description": "Successful Response"
|
|
7003
|
+
}
|
|
7004
|
+
},
|
|
7005
|
+
"summary": "Brain Quality Summary"
|
|
7006
|
+
}
|
|
7007
|
+
},
|
|
6944
7008
|
"/api/memory/clear": {
|
|
6945
7009
|
"post": {
|
|
6946
7010
|
"operationId": "memory_clear_api_memory_clear_post",
|
|
@@ -446,6 +446,7 @@ export const latticeApi = {
|
|
|
446
446
|
browserReadUrl: (url: string) => post("/api/browser/read-url", { url }, {}),
|
|
447
447
|
memoryManager: () => get("/api/memory/manager", { sources: [], tiers: [], usage: {} }),
|
|
448
448
|
memoryBrainQuality: () => get("/api/memory/brain-quality", {}),
|
|
449
|
+
memoryBrainProof: (query = "", limit = 3) => get("/api/memory/brain-proof", { proofs: {}, recall: { items: [] }, model_continuity: {}, claims: {} }, { q: query, limit }),
|
|
449
450
|
memoryRecall: (query: string, limit = 20) => post("/api/memory/recall", { query, limit }, { matches: [] }),
|
|
450
451
|
memoryCompact: () => post("/api/memory/compact", {}, {}),
|
|
451
452
|
memoryRebuild: () => post("/api/memory/rebuild", { target: "vector" }, {}),
|
|
@@ -1550,6 +1550,40 @@ export interface paths {
|
|
|
1550
1550
|
patch?: never;
|
|
1551
1551
|
trace?: never;
|
|
1552
1552
|
};
|
|
1553
|
+
"/api/memory/brain-proof": {
|
|
1554
|
+
parameters: {
|
|
1555
|
+
query?: never;
|
|
1556
|
+
header?: never;
|
|
1557
|
+
path?: never;
|
|
1558
|
+
cookie?: never;
|
|
1559
|
+
};
|
|
1560
|
+
/** Brain Proof */
|
|
1561
|
+
get: operations["brain_proof_api_memory_brain_proof_get"];
|
|
1562
|
+
put?: never;
|
|
1563
|
+
post?: never;
|
|
1564
|
+
delete?: never;
|
|
1565
|
+
options?: never;
|
|
1566
|
+
head?: never;
|
|
1567
|
+
patch?: never;
|
|
1568
|
+
trace?: never;
|
|
1569
|
+
};
|
|
1570
|
+
"/api/memory/brain-quality": {
|
|
1571
|
+
parameters: {
|
|
1572
|
+
query?: never;
|
|
1573
|
+
header?: never;
|
|
1574
|
+
path?: never;
|
|
1575
|
+
cookie?: never;
|
|
1576
|
+
};
|
|
1577
|
+
/** Brain Quality Summary */
|
|
1578
|
+
get: operations["brain_quality_summary_api_memory_brain_quality_get"];
|
|
1579
|
+
put?: never;
|
|
1580
|
+
post?: never;
|
|
1581
|
+
delete?: never;
|
|
1582
|
+
options?: never;
|
|
1583
|
+
head?: never;
|
|
1584
|
+
patch?: never;
|
|
1585
|
+
trace?: never;
|
|
1586
|
+
};
|
|
1553
1587
|
"/api/memory/clear": {
|
|
1554
1588
|
parameters: {
|
|
1555
1589
|
query?: never;
|
|
@@ -10322,6 +10356,58 @@ export interface operations {
|
|
|
10322
10356
|
};
|
|
10323
10357
|
};
|
|
10324
10358
|
};
|
|
10359
|
+
brain_proof_api_memory_brain_proof_get: {
|
|
10360
|
+
parameters: {
|
|
10361
|
+
query?: {
|
|
10362
|
+
q?: string;
|
|
10363
|
+
limit?: number;
|
|
10364
|
+
};
|
|
10365
|
+
header?: never;
|
|
10366
|
+
path?: never;
|
|
10367
|
+
cookie?: never;
|
|
10368
|
+
};
|
|
10369
|
+
requestBody?: never;
|
|
10370
|
+
responses: {
|
|
10371
|
+
/** @description Successful Response */
|
|
10372
|
+
200: {
|
|
10373
|
+
headers: {
|
|
10374
|
+
[name: string]: unknown;
|
|
10375
|
+
};
|
|
10376
|
+
content: {
|
|
10377
|
+
"application/json": unknown;
|
|
10378
|
+
};
|
|
10379
|
+
};
|
|
10380
|
+
/** @description Validation Error */
|
|
10381
|
+
422: {
|
|
10382
|
+
headers: {
|
|
10383
|
+
[name: string]: unknown;
|
|
10384
|
+
};
|
|
10385
|
+
content: {
|
|
10386
|
+
"application/json": components["schemas"]["HTTPValidationError"];
|
|
10387
|
+
};
|
|
10388
|
+
};
|
|
10389
|
+
};
|
|
10390
|
+
};
|
|
10391
|
+
brain_quality_summary_api_memory_brain_quality_get: {
|
|
10392
|
+
parameters: {
|
|
10393
|
+
query?: never;
|
|
10394
|
+
header?: never;
|
|
10395
|
+
path?: never;
|
|
10396
|
+
cookie?: never;
|
|
10397
|
+
};
|
|
10398
|
+
requestBody?: never;
|
|
10399
|
+
responses: {
|
|
10400
|
+
/** @description Successful Response */
|
|
10401
|
+
200: {
|
|
10402
|
+
headers: {
|
|
10403
|
+
[name: string]: unknown;
|
|
10404
|
+
};
|
|
10405
|
+
content: {
|
|
10406
|
+
"application/json": unknown;
|
|
10407
|
+
};
|
|
10408
|
+
};
|
|
10409
|
+
};
|
|
10410
|
+
};
|
|
10325
10411
|
memory_clear_api_memory_clear_post: {
|
|
10326
10412
|
parameters: {
|
|
10327
10413
|
query?: never;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { ImagePlus, Send } from "lucide-react";
|
|
2
|
+
import { FileUp, ImagePlus, Send } from "lucide-react";
|
|
3
3
|
import { Button } from "@/components/ui/button";
|
|
4
4
|
import { t, type Language } from "@/i18n";
|
|
5
5
|
|
|
@@ -8,16 +8,20 @@ export function BrainComposer({
|
|
|
8
8
|
draft,
|
|
9
9
|
streaming,
|
|
10
10
|
imageData,
|
|
11
|
+
uploadingDocument,
|
|
11
12
|
onDraftChange,
|
|
12
13
|
onImageDataChange,
|
|
14
|
+
onUploadDocument,
|
|
13
15
|
onSend,
|
|
14
16
|
}: {
|
|
15
17
|
language: Language;
|
|
16
18
|
draft: string;
|
|
17
19
|
streaming: boolean;
|
|
18
20
|
imageData: string | null;
|
|
21
|
+
uploadingDocument: boolean;
|
|
19
22
|
onDraftChange: (value: string) => void;
|
|
20
23
|
onImageDataChange: (value: string | null) => void;
|
|
24
|
+
onUploadDocument: (file: File) => void;
|
|
21
25
|
onSend: () => void;
|
|
22
26
|
}) {
|
|
23
27
|
return (
|
|
@@ -34,6 +38,21 @@ export function BrainComposer({
|
|
|
34
38
|
placeholder={t(language, "brain.placeholder")}
|
|
35
39
|
/>
|
|
36
40
|
<div className="brain-composer-actions">
|
|
41
|
+
<label className={`brain-document-input ${uploadingDocument ? "is-disabled" : ""}`}>
|
|
42
|
+
<FileUp className="h-3.5 w-3.5" />
|
|
43
|
+
<span>{uploadingDocument ? t(language, "brain.upload.uploading") : t(language, "brain.upload.ctaShort")}</span>
|
|
44
|
+
<input
|
|
45
|
+
type="file"
|
|
46
|
+
accept=".pdf,.docx,.xlsx,.pptx,.txt,.md,.csv,application/pdf,text/plain,text/markdown,text/csv"
|
|
47
|
+
className="sr-only"
|
|
48
|
+
disabled={uploadingDocument}
|
|
49
|
+
onChange={(event) => {
|
|
50
|
+
const file = event.target.files?.[0];
|
|
51
|
+
event.currentTarget.value = "";
|
|
52
|
+
if (file) onUploadDocument(file);
|
|
53
|
+
}}
|
|
54
|
+
/>
|
|
55
|
+
</label>
|
|
37
56
|
<label className="brain-image-input">
|
|
38
57
|
<ImagePlus className="h-3.5 w-3.5" />
|
|
39
58
|
<span>{t(language, "brain.image")}</span>
|
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import { DatabaseZap, ShieldCheck } from "lucide-react";
|
|
3
3
|
import { LanguageSwitcher } from "@/components/LanguageSwitcher";
|
|
4
4
|
import { t, type Language } from "@/i18n";
|
|
5
|
-
import type { BrainDepth, BrainReadiness, KnowledgeConcept, MemoryFragment, Message } from "./types";
|
|
5
|
+
import type { BrainDepth, BrainProof, BrainReadiness, KnowledgeConcept, MemoryFragment, Message } from "./types";
|
|
6
6
|
import { BrainCarePanel } from "./BrainCarePanel";
|
|
7
7
|
import { BrainComposer } from "./BrainComposer";
|
|
8
8
|
import { BrainOverviewPanel } from "./BrainOverviewPanel";
|
|
@@ -21,9 +21,12 @@ export function BrainConversation({
|
|
|
21
21
|
memories,
|
|
22
22
|
concepts,
|
|
23
23
|
readiness,
|
|
24
|
+
proof,
|
|
25
|
+
uploadingDocument,
|
|
24
26
|
onOpenDepth,
|
|
25
27
|
onDraftChange,
|
|
26
28
|
onImageDataChange,
|
|
29
|
+
onUploadDocument,
|
|
27
30
|
onSend,
|
|
28
31
|
}: {
|
|
29
32
|
language: Language;
|
|
@@ -39,9 +42,12 @@ export function BrainConversation({
|
|
|
39
42
|
memories: MemoryFragment[];
|
|
40
43
|
concepts: KnowledgeConcept[];
|
|
41
44
|
readiness: BrainReadiness;
|
|
45
|
+
proof: BrainProof;
|
|
46
|
+
uploadingDocument: boolean;
|
|
42
47
|
onOpenDepth: (depth: BrainDepth) => void;
|
|
43
48
|
onDraftChange: (value: string) => void;
|
|
44
49
|
onImageDataChange: (value: string | null) => void;
|
|
50
|
+
onUploadDocument: (file: File) => void;
|
|
45
51
|
onSend: () => void;
|
|
46
52
|
}) {
|
|
47
53
|
return (
|
|
@@ -69,10 +75,17 @@ export function BrainConversation({
|
|
|
69
75
|
memories={memories}
|
|
70
76
|
concepts={concepts}
|
|
71
77
|
readiness={readiness}
|
|
78
|
+
proof={proof}
|
|
72
79
|
onOpenDepth={onOpenDepth}
|
|
73
80
|
/>
|
|
74
81
|
{messages.length === 0 ? (
|
|
75
|
-
<BrainEmptyState
|
|
82
|
+
<BrainEmptyState
|
|
83
|
+
language={language}
|
|
84
|
+
starterPrompts={starterPrompts}
|
|
85
|
+
uploadingDocument={uploadingDocument}
|
|
86
|
+
onDraftChange={onDraftChange}
|
|
87
|
+
onUploadDocument={onUploadDocument}
|
|
88
|
+
/>
|
|
76
89
|
) : (
|
|
77
90
|
messages.map((message, index) => (
|
|
78
91
|
<div key={`${message.role}-${index}`} className={`brain-message ${message.role}`}>
|
|
@@ -97,8 +110,10 @@ export function BrainConversation({
|
|
|
97
110
|
draft={draft}
|
|
98
111
|
streaming={streaming}
|
|
99
112
|
imageData={imageData}
|
|
113
|
+
uploadingDocument={uploadingDocument}
|
|
100
114
|
onDraftChange={onDraftChange}
|
|
101
115
|
onImageDataChange={onImageDataChange}
|
|
116
|
+
onUploadDocument={onUploadDocument}
|
|
102
117
|
onSend={onSend}
|
|
103
118
|
/>
|
|
104
119
|
</section>
|
|
@@ -108,17 +123,37 @@ export function BrainConversation({
|
|
|
108
123
|
function BrainEmptyState({
|
|
109
124
|
language,
|
|
110
125
|
starterPrompts,
|
|
126
|
+
uploadingDocument,
|
|
111
127
|
onDraftChange,
|
|
128
|
+
onUploadDocument,
|
|
112
129
|
}: {
|
|
113
130
|
language: Language;
|
|
114
131
|
starterPrompts: string[];
|
|
132
|
+
uploadingDocument: boolean;
|
|
115
133
|
onDraftChange: (value: string) => void;
|
|
134
|
+
onUploadDocument: (file: File) => void;
|
|
116
135
|
}) {
|
|
117
136
|
return (
|
|
118
137
|
<div className="mind-empty">
|
|
119
138
|
<div className="mind-empty-kicker">{t(language, "brain.empty.kicker")}</div>
|
|
120
139
|
<div className="mind-empty-title">{t(language, "brain.empty.title")}</div>
|
|
121
140
|
<p>{t(language, "brain.empty.body")}</p>
|
|
141
|
+
<label className={`mind-empty-upload ${uploadingDocument ? "is-disabled" : ""}`}>
|
|
142
|
+
<DatabaseZap className="h-3.5 w-3.5" />
|
|
143
|
+
<span>{uploadingDocument ? t(language, "brain.upload.uploading") : t(language, "brain.upload.cta")}</span>
|
|
144
|
+
<input
|
|
145
|
+
type="file"
|
|
146
|
+
accept=".pdf,.docx,.xlsx,.pptx,.txt,.md,.csv,application/pdf,text/plain,text/markdown,text/csv"
|
|
147
|
+
className="sr-only"
|
|
148
|
+
disabled={uploadingDocument}
|
|
149
|
+
onChange={(event) => {
|
|
150
|
+
const file = event.target.files?.[0];
|
|
151
|
+
event.currentTarget.value = "";
|
|
152
|
+
if (file) onUploadDocument(file);
|
|
153
|
+
}}
|
|
154
|
+
/>
|
|
155
|
+
</label>
|
|
156
|
+
<small className="mind-empty-upload-hint">{t(language, "brain.upload.hint")}</small>
|
|
122
157
|
<div className="mind-empty-prompts" aria-label={t(language, "brain.aria.starterPrompts")}>
|
|
123
158
|
{starterPrompts.map((prompt) => (
|
|
124
159
|
<button key={prompt} type="button" onClick={() => onDraftChange(prompt)}>
|
|
@@ -5,7 +5,7 @@ import { type BrainState, LivingBrain, triggerBrainRecall } from "@/components/L
|
|
|
5
5
|
import { useAppStore } from "@/store/appStore";
|
|
6
6
|
import { t } from "@/i18n";
|
|
7
7
|
import { BrainConversation } from "./BrainConversation";
|
|
8
|
-
import { buildBrainReadiness, buildMemoryFragments, currentModelName, parseKnowledgeGraph } from "./brainData";
|
|
8
|
+
import { buildBrainProof, buildBrainReadiness, buildMemoryFragments, currentModelName, parseKnowledgeGraph } from "./brainData";
|
|
9
9
|
import { DepthEmergence } from "./DepthEmergence";
|
|
10
10
|
import { DEPTHS, type BrainDepth, type MemoryFragment, type Message } from "./types";
|
|
11
11
|
|
|
@@ -29,6 +29,8 @@ export function BrainHome({
|
|
|
29
29
|
const [graphSearch, setGraphSearch] = React.useState("");
|
|
30
30
|
const [selectedGraphId, setSelectedGraphId] = React.useState<string | null>(null);
|
|
31
31
|
const [memoryFeedback, setMemoryFeedback] = React.useState<string | null>(null);
|
|
32
|
+
const [uploadingDocument, setUploadingDocument] = React.useState(false);
|
|
33
|
+
const [lastRecallQuery, setLastRecallQuery] = React.useState("");
|
|
32
34
|
const streamRef = React.useRef<HTMLDivElement>(null);
|
|
33
35
|
const recallTimerRef = React.useRef<number | null>(null);
|
|
34
36
|
|
|
@@ -36,6 +38,10 @@ export function BrainHome({
|
|
|
36
38
|
const historyQ = useQuery({ queryKey: ["chatHistory"], queryFn: latticeApi.chatHistory });
|
|
37
39
|
const graphQ = useQuery({ queryKey: ["graph"], queryFn: latticeApi.graph });
|
|
38
40
|
const modelsQ = useQuery({ queryKey: ["models"], queryFn: latticeApi.models });
|
|
41
|
+
const brainProofQ = useQuery({
|
|
42
|
+
queryKey: ["memoryBrainProof", lastRecallQuery],
|
|
43
|
+
queryFn: () => latticeApi.memoryBrainProof(lastRecallQuery, 3),
|
|
44
|
+
});
|
|
39
45
|
|
|
40
46
|
const memoryFragments = React.useMemo(
|
|
41
47
|
() => buildMemoryFragments(memoriesQ.data?.data, historyQ.data?.data),
|
|
@@ -55,6 +61,10 @@ export function BrainHome({
|
|
|
55
61
|
[graphModel.edges],
|
|
56
62
|
);
|
|
57
63
|
const modelName = React.useMemo(() => currentModelName(modelsQ.data?.data), [modelsQ.data]);
|
|
64
|
+
const brainProof = React.useMemo(
|
|
65
|
+
() => buildBrainProof(brainProofQ.data?.data, modelName),
|
|
66
|
+
[brainProofQ.data, modelName],
|
|
67
|
+
);
|
|
58
68
|
const currentDepth = DEPTHS[explorationDepth - 1];
|
|
59
69
|
const starterPrompts = React.useMemo(
|
|
60
70
|
() => [
|
|
@@ -123,12 +133,39 @@ export function BrainHome({
|
|
|
123
133
|
});
|
|
124
134
|
} else {
|
|
125
135
|
setMemoryFeedback(t(language, "brain.saved", { topics: knowledgeConcepts.length, memories: memoryFragments.length }));
|
|
136
|
+
setLastRecallQuery(text);
|
|
126
137
|
}
|
|
127
138
|
} finally {
|
|
128
139
|
setStreaming(false);
|
|
129
140
|
void qc.invalidateQueries({ queryKey: ["chatHistory"] });
|
|
130
141
|
void qc.invalidateQueries({ queryKey: ["memoryManager"] });
|
|
131
142
|
void qc.invalidateQueries({ queryKey: ["graph"] });
|
|
143
|
+
void qc.invalidateQueries({ queryKey: ["memoryBrainProof"] });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function uploadDocument(file: File) {
|
|
148
|
+
if (uploadingDocument) return;
|
|
149
|
+
|
|
150
|
+
setUploadingDocument(true);
|
|
151
|
+
setMemoryFeedback(t(language, "brain.upload.pending", { name: file.name }));
|
|
152
|
+
onBrainChange("recalling", 0.86);
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
const result = await latticeApi.uploadDocument(file);
|
|
156
|
+
if (result.error) {
|
|
157
|
+
setMemoryFeedback(t(language, "brain.upload.failed", { reason: result.error }));
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
setMemoryFeedback(t(language, "brain.upload.saved", { name: file.name }));
|
|
162
|
+
setLastRecallQuery(file.name);
|
|
163
|
+
triggerBrainRecall();
|
|
164
|
+
void qc.invalidateQueries({ queryKey: ["memoryManager"] });
|
|
165
|
+
void qc.invalidateQueries({ queryKey: ["graph"] });
|
|
166
|
+
void qc.invalidateQueries({ queryKey: ["memoryBrainProof"] });
|
|
167
|
+
} finally {
|
|
168
|
+
setUploadingDocument(false);
|
|
132
169
|
}
|
|
133
170
|
}
|
|
134
171
|
|
|
@@ -242,9 +279,12 @@ export function BrainHome({
|
|
|
242
279
|
memories={memoryFragments}
|
|
243
280
|
concepts={knowledgeConcepts}
|
|
244
281
|
readiness={brainReadiness}
|
|
282
|
+
proof={brainProof}
|
|
283
|
+
uploadingDocument={uploadingDocument}
|
|
245
284
|
onOpenDepth={jumpToDepth}
|
|
246
285
|
onDraftChange={setDraft}
|
|
247
286
|
onImageDataChange={setImageData}
|
|
287
|
+
onUploadDocument={(file) => void uploadDocument(file)}
|
|
248
288
|
onSend={() => void send()}
|
|
249
289
|
/>
|
|
250
290
|
</main>
|
|
@@ -1,23 +1,31 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import { BrainCircuit, DatabaseZap, Repeat2 } from "lucide-react";
|
|
2
3
|
import { t } from "@/i18n";
|
|
3
4
|
import { useAppStore } from "@/store/appStore";
|
|
4
|
-
import type { BrainDepth, BrainReadiness, KnowledgeConcept, MemoryFragment } from "./types";
|
|
5
|
+
import type { BrainDepth, BrainProof, BrainReadiness, KnowledgeConcept, MemoryFragment } from "./types";
|
|
5
6
|
|
|
6
7
|
export function BrainOverviewPanel({
|
|
7
8
|
memories,
|
|
8
9
|
concepts,
|
|
9
10
|
readiness,
|
|
11
|
+
proof,
|
|
10
12
|
onOpenDepth,
|
|
11
13
|
}: {
|
|
12
14
|
memories: MemoryFragment[];
|
|
13
15
|
concepts: KnowledgeConcept[];
|
|
14
16
|
readiness: BrainReadiness;
|
|
17
|
+
proof: BrainProof;
|
|
15
18
|
onOpenDepth: (depth: BrainDepth) => void;
|
|
16
19
|
}) {
|
|
17
20
|
const language = useAppStore((state) => state.language);
|
|
18
21
|
const recent = memories.slice(0, 3);
|
|
19
22
|
const older = memories.slice(3, 6);
|
|
20
23
|
const topics = concepts.slice(0, 4);
|
|
24
|
+
const hasDurableProof = proof.proofs.hasDurableEvidence || proof.modelContinuity.proven;
|
|
25
|
+
const hasRecallProof = proof.recall.items.length > 0;
|
|
26
|
+
const modelProofValue = proof.modelContinuity.proven && proof.claims.keepsContextAcrossModels
|
|
27
|
+
? proof.modelContinuity.activeModel || t(language, "brain.proof.noModel")
|
|
28
|
+
: t(language, "brain.proof.modelPending");
|
|
21
29
|
|
|
22
30
|
return (
|
|
23
31
|
<section className="brain-overview-panel" aria-label={t(language, "brain.aria.overview")}>
|
|
@@ -60,10 +68,63 @@ export function BrainOverviewPanel({
|
|
|
60
68
|
{t(language, readiness.actionKey)}
|
|
61
69
|
</button>
|
|
62
70
|
</div>
|
|
71
|
+
<div className="brain-proof-strip" aria-label={t(language, "brain.proof.aria")}>
|
|
72
|
+
<BrainProofPoint
|
|
73
|
+
icon={<DatabaseZap className="h-4 w-4" />}
|
|
74
|
+
label={t(language, "brain.proof.context")}
|
|
75
|
+
value={hasDurableProof
|
|
76
|
+
? t(language, "brain.proof.contextValue", { count: proof.proofs.durableItems })
|
|
77
|
+
: t(language, "brain.proof.contextEmpty")}
|
|
78
|
+
/>
|
|
79
|
+
<BrainProofPoint
|
|
80
|
+
icon={<Repeat2 className="h-4 w-4" />}
|
|
81
|
+
label={t(language, "brain.proof.model")}
|
|
82
|
+
value={modelProofValue}
|
|
83
|
+
/>
|
|
84
|
+
<BrainProofPoint
|
|
85
|
+
icon={<BrainCircuit className="h-4 w-4" />}
|
|
86
|
+
label={t(language, "brain.proof.store")}
|
|
87
|
+
value={t(language, "brain.proof.storeValue", {
|
|
88
|
+
topics: proof.proofs.graphConcepts,
|
|
89
|
+
vectors: proof.proofs.vectorItems,
|
|
90
|
+
})}
|
|
91
|
+
/>
|
|
92
|
+
</div>
|
|
93
|
+
{hasRecallProof ? (
|
|
94
|
+
<button type="button" className="brain-recall-proof" onClick={() => onOpenDepth(2)}>
|
|
95
|
+
<span>{t(language, "brain.proof.recall")}</span>
|
|
96
|
+
<strong>{proof.recall.items[0].title}</strong>
|
|
97
|
+
<small>{proof.recall.items[0].snippet || proof.recall.query}</small>
|
|
98
|
+
</button>
|
|
99
|
+
) : (
|
|
100
|
+
<div className="brain-recall-proof is-empty" role="status">
|
|
101
|
+
<span>{t(language, "brain.proof.recallEmpty.kicker")}</span>
|
|
102
|
+
<strong>{t(language, hasDurableProof ? "brain.proof.recallPending" : "brain.proof.recallEmpty.title")}</strong>
|
|
103
|
+
<small>{t(language, hasDurableProof ? "brain.proof.recallPending.detail" : "brain.proof.recallEmpty.detail")}</small>
|
|
104
|
+
</div>
|
|
105
|
+
)}
|
|
63
106
|
</section>
|
|
64
107
|
);
|
|
65
108
|
}
|
|
66
109
|
|
|
110
|
+
function BrainProofPoint({
|
|
111
|
+
icon,
|
|
112
|
+
label,
|
|
113
|
+
value,
|
|
114
|
+
}: {
|
|
115
|
+
icon: React.ReactNode;
|
|
116
|
+
label: string;
|
|
117
|
+
value: string;
|
|
118
|
+
}) {
|
|
119
|
+
return (
|
|
120
|
+
<div className="brain-proof-point">
|
|
121
|
+
{icon}
|
|
122
|
+
<span>{label}</span>
|
|
123
|
+
<strong>{value}</strong>
|
|
124
|
+
</div>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
67
128
|
function BrainOverviewColumn({
|
|
68
129
|
title,
|
|
69
130
|
empty,
|