ltcai 6.3.1 → 6.5.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 +35 -41
- package/docs/CHANGELOG.md +59 -0
- package/frontend/src/api/client.ts +1 -0
- package/frontend/src/features/brain/BrainConversation.tsx +12 -3
- package/frontend/src/features/brain/BrainHome.tsx +21 -1
- package/frontend/src/features/brain/BrainMemoryLayer.tsx +8 -1
- package/frontend/src/features/brain/BrainOverviewPanel.tsx +15 -1
- package/frontend/src/features/brain/brainData.ts +72 -1
- package/frontend/src/features/brain/types.ts +15 -0
- package/frontend/src/i18n.ts +22 -0
- package/frontend/src/styles.css +128 -0
- package/lattice_brain/__init__.py +3 -1
- package/lattice_brain/quality.py +409 -0
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/knowledge_graph.py +71 -10
- package/latticeai/api/local_files.py +2 -0
- package/latticeai/api/memory.py +12 -6
- package/latticeai/api/search.py +9 -1
- package/latticeai/api/tools.py +3 -0
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/runtime/chat_wiring.py +1 -0
- package/latticeai/services/memory_service.py +130 -21
- package/latticeai/services/router_context.py +4 -0
- package/latticeai/services/search_service.py +61 -10
- 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-C1J_1441.js +16 -0
- package/static/app/assets/index-C1J_1441.js.map +1 -0
- package/static/app/assets/{index-Div5vMlq.css → index-CdCVz_i4.css} +1 -1
- package/static/app/index.html +2 -2
- package/static/app/assets/index-t1jx1BR9.js +0 -16
- package/static/app/assets/index-t1jx1BR9.js.map +0 -1
package/README.md
CHANGED
|
@@ -69,42 +69,42 @@ You need Lattice AI when:
|
|
|
69
69
|
Choose the owner of the Brain. The profile is not a SaaS account by default; it
|
|
70
70
|
is the local identity for the knowledge you keep.
|
|
71
71
|
|
|
72
|
-

|
|
73
73
|
|
|
74
74
|
### 2. Environment Analysis
|
|
75
75
|
|
|
76
76
|
See what kind of local AI experience this computer can support before choosing a
|
|
77
77
|
model.
|
|
78
78
|
|
|
79
|
-

|
|
80
80
|
|
|
81
81
|
### 3. Recommended Models
|
|
82
82
|
|
|
83
83
|
Start with a short list: safest recommendation, faster model, stronger model.
|
|
84
84
|
Advanced details stay available without overwhelming first-time users.
|
|
85
85
|
|
|
86
|
-

|
|
87
87
|
|
|
88
88
|
### 4. Install And Load
|
|
89
89
|
|
|
90
90
|
Download and load only after consent. Lattice explains model size, local
|
|
91
91
|
execution, and network use before work starts.
|
|
92
92
|
|
|
93
|
-

|
|
94
94
|
|
|
95
95
|
### 5. Brain Chat
|
|
96
96
|
|
|
97
97
|
Talk normally. Useful decisions and context become memory, then appear later as
|
|
98
98
|
topics, relationships, and graph structure.
|
|
99
99
|
|
|
100
|
-

|
|
101
101
|
|
|
102
102
|
### 6. Review Center
|
|
103
103
|
|
|
104
104
|
Automation results are staged for review before they become durable decisions.
|
|
105
105
|
Snooze, unsnooze, run now, approve, and dismiss actions stay explicit.
|
|
106
106
|
|
|
107
|
-

|
|
108
108
|
|
|
109
109
|
## Brain Depths
|
|
110
110
|
|
|
@@ -120,10 +120,10 @@ The user travels inward from everyday memory to deeper structure:
|
|
|
120
120
|
|
|
121
121
|
Walkthrough:
|
|
122
122
|
|
|
123
|
-

|
|
124
124
|
|
|
125
125
|
Screenshot index and capture notes:
|
|
126
|
-
[output/release/v6.
|
|
126
|
+
[output/release/v6.5.0/SCREENSHOT_INDEX.md](output/release/v6.5.0/SCREENSHOT_INDEX.md)
|
|
127
127
|
|
|
128
128
|
## Install
|
|
129
129
|
|
|
@@ -200,39 +200,31 @@ 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
|
-
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
Expected artifacts for 6.3.1 release must use exact filenames:
|
|
230
|
-
|
|
231
|
-
- `dist/ltcai-6.3.1-py3-none-any.whl`
|
|
232
|
-
- `dist/ltcai-6.3.1.tar.gz`
|
|
233
|
-
- `ltcai-6.3.1.tgz`
|
|
234
|
-
- `dist/ltcai-6.3.1.vsix`
|
|
235
|
-
- `src-tauri/target/release/bundle/dmg/Lattice AI_6.3.1_aarch64.dmg`
|
|
203
|
+
The current development target is **6.5.0 Brain Experience Readiness**:
|
|
204
|
+
|
|
205
|
+
- Brain Home now gives normal users a readiness signal that explains whether
|
|
206
|
+
the Brain is waiting for its first memory, turning memories into topics, or
|
|
207
|
+
ready to explore as a connected map. That signal now comes from the backend
|
|
208
|
+
Memory Manager summary instead of a frontend-only estimate.
|
|
209
|
+
- The Living Brain surface now includes a persistent depth progress rail, so
|
|
210
|
+
users can understand and reverse the path from conversation to memory,
|
|
211
|
+
topics, relationships, and the full knowledge graph.
|
|
212
|
+
- Memory-save feedback now explicitly states that saved context is available
|
|
213
|
+
later with source-aware recall, closing the gap between "chat happened" and
|
|
214
|
+
"my Brain became more useful."
|
|
215
|
+
- The release keeps the 6.4.0 quality hardening intact and focuses on
|
|
216
|
+
end-user comprehension rather than adding a new storage or graph schema.
|
|
217
|
+
- The release remains local-first: no automatic web/email/calendar ingestion,
|
|
218
|
+
no package publishing, no production deployment, and no external reranker or
|
|
219
|
+
embedding API use without explicit opt-in.
|
|
220
|
+
|
|
221
|
+
Expected artifacts for 6.5.0 release must use exact filenames:
|
|
222
|
+
|
|
223
|
+
- `dist/ltcai-6.5.0-py3-none-any.whl`
|
|
224
|
+
- `dist/ltcai-6.5.0.tar.gz`
|
|
225
|
+
- `ltcai-6.5.0.tgz`
|
|
226
|
+
- `dist/ltcai-6.5.0.vsix`
|
|
227
|
+
- `src-tauri/target/release/bundle/dmg/Lattice AI_6.5.0_aarch64.dmg`
|
|
236
228
|
|
|
237
229
|
Do not upload `dist/*`. Package registry publishing remains owner-run.
|
|
238
230
|
|
|
@@ -251,6 +243,8 @@ Do not upload `dist/*`. Package registry publishing remains owner-run.
|
|
|
251
243
|
|
|
252
244
|
| Version | Theme |
|
|
253
245
|
| --- | --- |
|
|
246
|
+
| 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
|
+
| 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 |
|
|
254
248
|
| 6.3.1 | Access Runtime / i18n Follow-up: app-factory access-control extraction, focused access runtime tests, and Capture/Review Center i18n coverage |
|
|
255
249
|
| 6.3.0 | Product Hardening Completion: Brain archive/provenance/ingestion UX polish, Review Center Run Now contract hardening, local model runtime status, app-factory review wiring, shim smoke, i18n guard, and exact release artifacts |
|
|
256
250
|
| 6.2.0 | Product Decomposition / Release Smoke Automation: App and ProductFlow feature extraction, legacy root shim shrink, model download consent UX, typed router contexts, localized smoke tests, and wheel/npm/static/Tauri release smoke |
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,65 @@
|
|
|
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.5.0] - 2026-06-18
|
|
7
|
+
|
|
8
|
+
> Brain Experience Readiness. Uses the 6.4.0 quality baseline to improve what
|
|
9
|
+
> normal users can understand: how alive the Brain is, where they are in the
|
|
10
|
+
> memory-to-graph journey, and what changed after a useful exchange is saved.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Brain Home readiness strip that classifies the current Brain as waiting for
|
|
14
|
+
its first memory, forming topics, or ready for map exploration.
|
|
15
|
+
- Backend-owned Brain readiness summary from `MemoryService`, exposed through
|
|
16
|
+
`/api/memory/manager` and `/api/memory/brain-quality`, so the UI no longer
|
|
17
|
+
has to infer Brain growth from local fragments.
|
|
18
|
+
- Persistent depth progress rail across the Living Brain surface so the
|
|
19
|
+
memory -> topics -> relationships -> graph journey is visible and reversible.
|
|
20
|
+
- Source-aware memory-save detail under chat feedback, making the recall loop
|
|
21
|
+
explicit after a conversation enriches the Brain.
|
|
22
|
+
- Visual regression assertions for Brain readiness, depth progress, and
|
|
23
|
+
source-aware memory-save feedback.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- Brain overview empty states now guide first-memory use without exposing graph
|
|
27
|
+
internals or administrator language.
|
|
28
|
+
- Brain readiness now uses the same memory, graph, relationship, and source
|
|
29
|
+
health signals that the Memory Manager reports, keeping product UX aligned
|
|
30
|
+
with backend Brain state.
|
|
31
|
+
- Package/runtime/static metadata is synchronized to 6.5.0; package publish and
|
|
32
|
+
deployment remain owner-run only.
|
|
33
|
+
|
|
34
|
+
## [6.4.0] - 2026-06-17
|
|
35
|
+
|
|
36
|
+
> Digital Brain Quality Hardening. Tightens workspace-scoped Brain retrieval
|
|
37
|
+
> and memory mutation boundaries while adding non-destructive quality primitives
|
|
38
|
+
> for embeddings, retrieval, memory, graph validation, context assembly, and
|
|
39
|
+
> benchmarks.
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
- `lattice_brain.quality` with embedding fallback labelling, drift/re-index
|
|
43
|
+
planning, BM25 lexical scoring, hybrid fusion, reranker fallback contracts,
|
|
44
|
+
memory candidate scoring/deduplication/conflict/retention helpers, graph edge
|
|
45
|
+
confidence/evidence metrics, structured context guardrails, and retrieval
|
|
46
|
+
benchmark metric calculation.
|
|
47
|
+
- `docs/v6.4/BRAIN_QUALITY_BASELINE.md` documenting the 6.4.0 Digital Brain
|
|
48
|
+
quality baseline, risk register, validation items, and deferred work.
|
|
49
|
+
- Refreshed README release evidence screenshots and walkthrough GIF under
|
|
50
|
+
`output/release/v6.4.0/`.
|
|
51
|
+
- Unit coverage for Brain quality primitives and workspace-scoped graph/search
|
|
52
|
+
and memory-manager mutation boundaries.
|
|
53
|
+
|
|
54
|
+
### Changed
|
|
55
|
+
- `/api/graph*`, `/knowledge-graph/*`, and hybrid-search service paths now
|
|
56
|
+
preserve workspace scope across graph, node, neighborhood, relationship,
|
|
57
|
+
keyword, vector, graph, and hybrid retrieval.
|
|
58
|
+
- Memory Manager prune, compact, and clear operations now intersect requested
|
|
59
|
+
ids/kinds with the caller's scoped memory set.
|
|
60
|
+
- Memory Manager graph clear is blocked because the existing graph clear path
|
|
61
|
+
is not workspace-scoped.
|
|
62
|
+
- Package/runtime/static metadata is synchronized to 6.4.0; package publish and
|
|
63
|
+
deployment remain owner-run only.
|
|
64
|
+
|
|
6
65
|
## [6.3.1] - 2026-06-17
|
|
7
66
|
|
|
8
67
|
> Access Runtime / i18n Follow-up. Closes the next app-factory decomposition
|
|
@@ -445,6 +445,7 @@ export const latticeApi = {
|
|
|
445
445
|
},
|
|
446
446
|
browserReadUrl: (url: string) => post("/api/browser/read-url", { url }, {}),
|
|
447
447
|
memoryManager: () => get("/api/memory/manager", { sources: [], tiers: [], usage: {} }),
|
|
448
|
+
memoryBrainQuality: () => get("/api/memory/brain-quality", {}),
|
|
448
449
|
memoryRecall: (query: string, limit = 20) => post("/api/memory/recall", { query, limit }, { matches: [] }),
|
|
449
450
|
memoryCompact: () => post("/api/memory/compact", {}, {}),
|
|
450
451
|
memoryRebuild: () => post("/api/memory/rebuild", { target: "vector" }, {}),
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { ShieldCheck } from "lucide-react";
|
|
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, KnowledgeConcept, MemoryFragment, Message } from "./types";
|
|
5
|
+
import type { BrainDepth, BrainReadiness, KnowledgeConcept, MemoryFragment, Message } from "./types";
|
|
6
6
|
import { BrainCarePanel } from "./BrainCarePanel";
|
|
7
7
|
import { BrainComposer } from "./BrainComposer";
|
|
8
8
|
import { BrainOverviewPanel } from "./BrainOverviewPanel";
|
|
@@ -20,6 +20,7 @@ export function BrainConversation({
|
|
|
20
20
|
streamRef,
|
|
21
21
|
memories,
|
|
22
22
|
concepts,
|
|
23
|
+
readiness,
|
|
23
24
|
onOpenDepth,
|
|
24
25
|
onDraftChange,
|
|
25
26
|
onImageDataChange,
|
|
@@ -37,6 +38,7 @@ export function BrainConversation({
|
|
|
37
38
|
streamRef: React.RefObject<HTMLDivElement | null>;
|
|
38
39
|
memories: MemoryFragment[];
|
|
39
40
|
concepts: KnowledgeConcept[];
|
|
41
|
+
readiness: BrainReadiness;
|
|
40
42
|
onOpenDepth: (depth: BrainDepth) => void;
|
|
41
43
|
onDraftChange: (value: string) => void;
|
|
42
44
|
onImageDataChange: (value: string | null) => void;
|
|
@@ -66,6 +68,7 @@ export function BrainConversation({
|
|
|
66
68
|
<BrainOverviewPanel
|
|
67
69
|
memories={memories}
|
|
68
70
|
concepts={concepts}
|
|
71
|
+
readiness={readiness}
|
|
69
72
|
onOpenDepth={onOpenDepth}
|
|
70
73
|
/>
|
|
71
74
|
{messages.length === 0 ? (
|
|
@@ -79,7 +82,13 @@ export function BrainConversation({
|
|
|
79
82
|
)}
|
|
80
83
|
</div>
|
|
81
84
|
|
|
82
|
-
{memoryFeedback ?
|
|
85
|
+
{memoryFeedback ? (
|
|
86
|
+
<div className="brain-save-feedback" role="status">
|
|
87
|
+
<DatabaseZap className="h-3.5 w-3.5" />
|
|
88
|
+
<span>{memoryFeedback}</span>
|
|
89
|
+
<small>{t(language, "brain.saved.detail")}</small>
|
|
90
|
+
</div>
|
|
91
|
+
) : null}
|
|
83
92
|
|
|
84
93
|
<BrainCarePanel language={language} />
|
|
85
94
|
|
|
@@ -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 { buildMemoryFragments, currentModelName, parseKnowledgeGraph } from "./brainData";
|
|
8
|
+
import { 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
|
|
|
@@ -46,6 +46,10 @@ export function BrainHome({
|
|
|
46
46
|
() => graphModel.nodes.slice(0, 10),
|
|
47
47
|
[graphModel.nodes],
|
|
48
48
|
);
|
|
49
|
+
const brainReadiness = React.useMemo(
|
|
50
|
+
() => buildBrainReadiness(memoriesQ.data?.data, memoryFragments.length, knowledgeConcepts.length),
|
|
51
|
+
[knowledgeConcepts.length, memoriesQ.data, memoryFragments.length],
|
|
52
|
+
);
|
|
49
53
|
const relationshipThreads = React.useMemo(
|
|
50
54
|
() => graphModel.edges.slice(0, 10),
|
|
51
55
|
[graphModel.edges],
|
|
@@ -186,6 +190,21 @@ export function BrainHome({
|
|
|
186
190
|
<button type="button" className={explorationDepth === 5 ? "is-active" : ""} onClick={() => jumpToDepth(5)}>{t(language, "brain.view.graph")}</button>
|
|
187
191
|
</div>
|
|
188
192
|
|
|
193
|
+
<div className="brain-depth-rail" aria-label={t(language, "brain.depthRail.aria")}>
|
|
194
|
+
{DEPTHS.map((depth) => (
|
|
195
|
+
<button
|
|
196
|
+
key={depth.level}
|
|
197
|
+
type="button"
|
|
198
|
+
className={depth.level <= explorationDepth ? "is-revealed" : ""}
|
|
199
|
+
aria-current={depth.level === explorationDepth ? "step" : undefined}
|
|
200
|
+
onClick={() => jumpToDepth(depth.level)}
|
|
201
|
+
>
|
|
202
|
+
<span>{depth.level}</span>
|
|
203
|
+
<strong>{t(language, `brain.depth.${depth.level}`)}</strong>
|
|
204
|
+
</button>
|
|
205
|
+
))}
|
|
206
|
+
</div>
|
|
207
|
+
|
|
189
208
|
<div className="brain-field-layer" aria-hidden={explorationDepth < 2}>
|
|
190
209
|
<DepthEmergence
|
|
191
210
|
depth={explorationDepth}
|
|
@@ -222,6 +241,7 @@ export function BrainHome({
|
|
|
222
241
|
streamRef={streamRef}
|
|
223
242
|
memories={memoryFragments}
|
|
224
243
|
concepts={knowledgeConcepts}
|
|
244
|
+
readiness={brainReadiness}
|
|
225
245
|
onOpenDepth={jumpToDepth}
|
|
226
246
|
onDraftChange={setDraft}
|
|
227
247
|
onImageDataChange={setImageData}
|
|
@@ -14,7 +14,14 @@ export function BrainMemoryLayer({
|
|
|
14
14
|
}) {
|
|
15
15
|
const language = useAppStore((state) => state.language);
|
|
16
16
|
const visible = memories.slice(0, depth >= 3 ? 8 : 6);
|
|
17
|
-
if (!visible.length)
|
|
17
|
+
if (!visible.length) {
|
|
18
|
+
return (
|
|
19
|
+
<div className="memory-fragment is-empty">
|
|
20
|
+
<span>{t(language, "brain.memory.empty.kicker")}</span>
|
|
21
|
+
<strong>{t(language, "brain.memory.empty")}</strong>
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
18
25
|
|
|
19
26
|
return (
|
|
20
27
|
<>
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { t } from "@/i18n";
|
|
3
3
|
import { useAppStore } from "@/store/appStore";
|
|
4
|
-
import type { BrainDepth, KnowledgeConcept, MemoryFragment } from "./types";
|
|
4
|
+
import type { BrainDepth, BrainReadiness, KnowledgeConcept, MemoryFragment } from "./types";
|
|
5
5
|
|
|
6
6
|
export function BrainOverviewPanel({
|
|
7
7
|
memories,
|
|
8
8
|
concepts,
|
|
9
|
+
readiness,
|
|
9
10
|
onOpenDepth,
|
|
10
11
|
}: {
|
|
11
12
|
memories: MemoryFragment[];
|
|
12
13
|
concepts: KnowledgeConcept[];
|
|
14
|
+
readiness: BrainReadiness;
|
|
13
15
|
onOpenDepth: (depth: BrainDepth) => void;
|
|
14
16
|
}) {
|
|
15
17
|
const language = useAppStore((state) => state.language);
|
|
@@ -46,6 +48,18 @@ export function BrainOverviewPanel({
|
|
|
46
48
|
onOpen={() => onOpenDepth(3)}
|
|
47
49
|
/>
|
|
48
50
|
</div>
|
|
51
|
+
<div className="brain-readiness-strip" data-state={readiness.state}>
|
|
52
|
+
<div>
|
|
53
|
+
<span>{t(language, "brain.readiness.kicker")}</span>
|
|
54
|
+
<strong>{t(language, readiness.titleKey)}</strong>
|
|
55
|
+
</div>
|
|
56
|
+
<div className="brain-readiness-meter" aria-label={t(language, "brain.readiness.aria")}>
|
|
57
|
+
<i style={{ width: `${readiness.score}%` }} />
|
|
58
|
+
</div>
|
|
59
|
+
<button type="button" onClick={() => onOpenDepth(readiness.depth)}>
|
|
60
|
+
{t(language, readiness.actionKey)}
|
|
61
|
+
</button>
|
|
62
|
+
</div>
|
|
49
63
|
</section>
|
|
50
64
|
);
|
|
51
65
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { asArray } from "@/lib/utils";
|
|
2
|
-
import type { ApiRecord, KnowledgeConcept, KnowledgeGraphModel, MemoryFragment, RelationshipThread } from "./types";
|
|
2
|
+
import type { ApiRecord, BrainDepth, BrainReadiness, KnowledgeConcept, KnowledgeGraphModel, MemoryFragment, RelationshipThread } from "./types";
|
|
3
3
|
import { clamp } from "./graphLayout";
|
|
4
4
|
|
|
5
5
|
export function buildMemoryFragments(memoryData: unknown, historyData: unknown): MemoryFragment[] {
|
|
@@ -60,6 +60,32 @@ export function currentModelName(data: unknown) {
|
|
|
60
60
|
return firstLoaded ? textValue(firstLoaded, ["name", "id", "model_id"], "local mind") : "local mind";
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
export function buildBrainReadiness(memoryData: unknown, fallbackMemoryCount: number, fallbackConceptCount: number): BrainReadiness {
|
|
64
|
+
const memory = isRecord(memoryData) ? memoryData : {};
|
|
65
|
+
const backend = isRecord(memory.brain_readiness) ? memory.brain_readiness : {};
|
|
66
|
+
const backendState = textValue(backend, ["state"]);
|
|
67
|
+
const backendDepth = numberValue(backend, ["depth"]);
|
|
68
|
+
const backendScore = numberValue(backend, ["score"]);
|
|
69
|
+
if ((backendState === "quiet" || backendState === "forming" || backendState === "alive") && isBrainDepth(backendDepth)) {
|
|
70
|
+
const signals = isRecord(backend.signals) ? backend.signals : {};
|
|
71
|
+
return {
|
|
72
|
+
score: clamp(Math.round(backendScore || 0), 0, 100),
|
|
73
|
+
state: backendState,
|
|
74
|
+
depth: backendDepth,
|
|
75
|
+
titleKey: textValue(backend, ["title_key", "titleKey"], `brain.readiness.${backendState}`),
|
|
76
|
+
actionKey: textValue(backend, ["action_key", "actionKey"], readinessActionKey(backendState)),
|
|
77
|
+
source: "memory_service",
|
|
78
|
+
signals: {
|
|
79
|
+
memoryCount: numberValue(signals, ["memory_count", "memoryCount"]),
|
|
80
|
+
conceptCount: numberValue(signals, ["concept_count", "conceptCount"]),
|
|
81
|
+
relationshipCount: numberValue(signals, ["relationship_count", "relationshipCount"]),
|
|
82
|
+
healthySources: numberValue(signals, ["healthy_sources", "healthySources"]),
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return fallbackBrainReadiness(fallbackMemoryCount, fallbackConceptCount);
|
|
87
|
+
}
|
|
88
|
+
|
|
63
89
|
function uniqueById<T extends { id: string }>(items: T[]) {
|
|
64
90
|
const seen = new Set<string>();
|
|
65
91
|
return items.filter((item) => {
|
|
@@ -69,6 +95,51 @@ function uniqueById<T extends { id: string }>(items: T[]) {
|
|
|
69
95
|
});
|
|
70
96
|
}
|
|
71
97
|
|
|
98
|
+
function fallbackBrainReadiness(memoryCount: number, conceptCount: number): BrainReadiness {
|
|
99
|
+
const score = Math.min(100, Math.round(memoryCount * 12 + conceptCount * 10));
|
|
100
|
+
if (memoryCount < 1) {
|
|
101
|
+
return {
|
|
102
|
+
score: Math.max(12, score),
|
|
103
|
+
state: "quiet",
|
|
104
|
+
depth: 2,
|
|
105
|
+
titleKey: "brain.readiness.quiet",
|
|
106
|
+
actionKey: "brain.readiness.start",
|
|
107
|
+
source: "frontend_fallback",
|
|
108
|
+
signals: { memoryCount, conceptCount, relationshipCount: 0, healthySources: 0 },
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (conceptCount < 3) {
|
|
112
|
+
return {
|
|
113
|
+
score: Math.max(38, score),
|
|
114
|
+
state: "forming",
|
|
115
|
+
depth: 3,
|
|
116
|
+
titleKey: "brain.readiness.forming",
|
|
117
|
+
actionKey: "brain.readiness.grow",
|
|
118
|
+
source: "frontend_fallback",
|
|
119
|
+
signals: { memoryCount, conceptCount, relationshipCount: 0, healthySources: 0 },
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
score: Math.max(72, score),
|
|
124
|
+
state: "alive",
|
|
125
|
+
depth: 5,
|
|
126
|
+
titleKey: "brain.readiness.alive",
|
|
127
|
+
actionKey: "brain.readiness.map",
|
|
128
|
+
source: "frontend_fallback",
|
|
129
|
+
signals: { memoryCount, conceptCount, relationshipCount: 0, healthySources: 0 },
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function readinessActionKey(state: "quiet" | "forming" | "alive") {
|
|
134
|
+
if (state === "quiet") return "brain.readiness.start";
|
|
135
|
+
if (state === "forming") return "brain.readiness.grow";
|
|
136
|
+
return "brain.readiness.map";
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function isBrainDepth(value: number): value is BrainDepth {
|
|
140
|
+
return value >= 1 && value <= 5 && Number.isInteger(value);
|
|
141
|
+
}
|
|
142
|
+
|
|
72
143
|
export function isRecord(value: unknown): value is ApiRecord {
|
|
73
144
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
74
145
|
}
|
|
@@ -35,6 +35,21 @@ export type KnowledgeGraphModel = {
|
|
|
35
35
|
edges: RelationshipThread[];
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
+
export type BrainReadiness = {
|
|
39
|
+
score: number;
|
|
40
|
+
state: "quiet" | "forming" | "alive";
|
|
41
|
+
depth: BrainDepth;
|
|
42
|
+
titleKey: string;
|
|
43
|
+
actionKey: string;
|
|
44
|
+
source: "memory_service" | "frontend_fallback";
|
|
45
|
+
signals: {
|
|
46
|
+
memoryCount: number;
|
|
47
|
+
conceptCount: number;
|
|
48
|
+
relationshipCount: number;
|
|
49
|
+
healthySources: number;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
|
|
38
53
|
export const DEPTHS: Array<{ level: BrainDepth; labelKey: string; state: BrainState }> = [
|
|
39
54
|
{ level: 1, labelKey: "brain.depthLabel.1", state: "idle" },
|
|
40
55
|
{ level: 2, labelKey: "brain.depthLabel.2", state: "recalling" },
|
package/frontend/src/i18n.ts
CHANGED
|
@@ -31,6 +31,7 @@ export const COPY: Record<Language, TextMap> = {
|
|
|
31
31
|
"brain.aria.home": "Lattice Brain",
|
|
32
32
|
"brain.aria.exploration": "Brain 탐색",
|
|
33
33
|
"brain.aria.quickViews": "Brain 빠른 보기",
|
|
34
|
+
"brain.depthRail.aria": "Brain 깊이 진행 상태",
|
|
34
35
|
"brain.aria.conversation": "대화",
|
|
35
36
|
"brain.aria.ownership": "Brain 소유 보장",
|
|
36
37
|
"brain.aria.starterPrompts": "시작 프롬프트",
|
|
@@ -57,6 +58,7 @@ export const COPY: Record<Language, TextMap> = {
|
|
|
57
58
|
"brain.imageAttached": "이미지 첨부됨",
|
|
58
59
|
"brain.send": "보내기",
|
|
59
60
|
"brain.saved": "기억에 저장됨 · 연결된 주제 {topics}개 · 관련 기억 {memories}개",
|
|
61
|
+
"brain.saved.detail": "출처와 함께 나중에 다시 불러올 수 있습니다.",
|
|
60
62
|
"brain.recalled": "기억을 다시 꺼냈습니다: {title}",
|
|
61
63
|
"brain.overview.kicker": "Brain 한눈에 보기",
|
|
62
64
|
"brain.overview.title": "기억과 주제를 바로 확인하세요.",
|
|
@@ -67,6 +69,15 @@ export const COPY: Record<Language, TextMap> = {
|
|
|
67
69
|
"brain.overview.olderEmpty": "대화가 쌓이면 과거 기억이 보입니다.",
|
|
68
70
|
"brain.overview.topics": "주요 주제",
|
|
69
71
|
"brain.overview.topicsEmpty": "주제가 형성되는 중입니다.",
|
|
72
|
+
"brain.readiness.kicker": "Brain 준비도",
|
|
73
|
+
"brain.readiness.aria": "Brain 준비도 점수",
|
|
74
|
+
"brain.readiness.quiet": "첫 기억을 기다리고 있습니다.",
|
|
75
|
+
"brain.readiness.forming": "기억이 주제로 자라는 중입니다.",
|
|
76
|
+
"brain.readiness.alive": "기억과 관계가 연결되어 있습니다.",
|
|
77
|
+
"brain.readiness.start": "첫 기억 보기",
|
|
78
|
+
"brain.readiness.grow": "주제 키우기",
|
|
79
|
+
"brain.readiness.map": "지도 보기",
|
|
80
|
+
"brain.memory.empty.kicker": "첫 기억",
|
|
70
81
|
"brain.memory.empty": "기억이 조용합니다.",
|
|
71
82
|
"brain.knowledge.empty": "지식이 형성되는 중입니다.",
|
|
72
83
|
"brain.graph.empty": "아직 맞는 지식이 없습니다.",
|
|
@@ -352,6 +363,7 @@ export const COPY: Record<Language, TextMap> = {
|
|
|
352
363
|
"brain.aria.home": "Lattice Brain",
|
|
353
364
|
"brain.aria.exploration": "Brain exploration",
|
|
354
365
|
"brain.aria.quickViews": "Brain quick views",
|
|
366
|
+
"brain.depthRail.aria": "Brain depth progress",
|
|
355
367
|
"brain.aria.conversation": "Conversation",
|
|
356
368
|
"brain.aria.ownership": "Brain ownership guarantees",
|
|
357
369
|
"brain.aria.starterPrompts": "Starter prompts",
|
|
@@ -378,6 +390,7 @@ export const COPY: Record<Language, TextMap> = {
|
|
|
378
390
|
"brain.imageAttached": "Image attached",
|
|
379
391
|
"brain.send": "Send",
|
|
380
392
|
"brain.saved": "Saved to memory · {topics} linked topics · {memories} related memories",
|
|
393
|
+
"brain.saved.detail": "Available later with source-aware recall.",
|
|
381
394
|
"brain.recalled": "Recalled this memory: {title}",
|
|
382
395
|
"brain.overview.kicker": "Brain at a glance",
|
|
383
396
|
"brain.overview.title": "See memories and topics immediately.",
|
|
@@ -388,6 +401,15 @@ export const COPY: Record<Language, TextMap> = {
|
|
|
388
401
|
"brain.overview.olderEmpty": "Earlier memories appear as conversations accumulate.",
|
|
389
402
|
"brain.overview.topics": "Main topics",
|
|
390
403
|
"brain.overview.topicsEmpty": "Topics are still forming.",
|
|
404
|
+
"brain.readiness.kicker": "Brain readiness",
|
|
405
|
+
"brain.readiness.aria": "Brain readiness score",
|
|
406
|
+
"brain.readiness.quiet": "Waiting for the first memory.",
|
|
407
|
+
"brain.readiness.forming": "Memories are becoming topics.",
|
|
408
|
+
"brain.readiness.alive": "Memories and relationships are connected.",
|
|
409
|
+
"brain.readiness.start": "View first memory",
|
|
410
|
+
"brain.readiness.grow": "Grow topics",
|
|
411
|
+
"brain.readiness.map": "Open map",
|
|
412
|
+
"brain.memory.empty.kicker": "First memory",
|
|
391
413
|
"brain.memory.empty": "Memory is quiet",
|
|
392
414
|
"brain.knowledge.empty": "Knowledge is forming",
|
|
393
415
|
"brain.graph.empty": "No matching knowledge yet",
|