ltcai 11.0.1 → 11.2.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 +55 -43
- package/docs/CHANGELOG.md +61 -0
- package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
- package/docs/DEVELOPMENT.md +1 -1
- package/docs/FEATURE_AUDIT_v11.2.0.md +393 -0
- package/docs/LAYOUT_REBUILD_SPEC.md +9 -1
- package/docs/ONBOARDING.md +1 -1
- package/docs/OPERATIONS.md +1 -1
- package/docs/PERFORMANCE.md +71 -18
- package/docs/TRUST_MODEL.md +1 -1
- package/docs/WHY_LATTICE.md +1 -1
- package/docs/architecture.md +6 -2
- package/docs/kg-schema.md +1 -1
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/embeddings.py +12 -37
- package/lattice_brain/gates.py +125 -0
- package/lattice_brain/graph/discovery_index.py +30 -32
- package/lattice_brain/graph/fusion.py +35 -4
- package/lattice_brain/graph/image_vectors.py +230 -0
- package/lattice_brain/graph/ingest.py +11 -5
- package/lattice_brain/graph/projection.py +66 -8
- package/lattice_brain/graph/provenance.py +27 -2
- package/lattice_brain/graph/retrieval.py +113 -2
- package/lattice_brain/graph/retrieval_docgen.py +6 -6
- package/lattice_brain/graph/schema.py +18 -0
- package/lattice_brain/graph/store.py +9 -0
- package/lattice_brain/graph/vector_index/selector.py +32 -2
- package/lattice_brain/ingestion.py +363 -10
- package/lattice_brain/multimodal.py +1258 -0
- package/lattice_brain/portability.py +169 -32
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/lattice_brain/sealed_box.py +244 -0
- package/lattice_brain/self_model.py +77 -22
- package/lattice_brain/synthesis.py +24 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/brain_intelligence.py +4 -0
- package/latticeai/api/chat.py +11 -0
- package/latticeai/api/chat_helpers.py +16 -3
- package/latticeai/api/chat_hybrid.py +32 -1
- package/latticeai/api/features.py +70 -0
- package/latticeai/api/local_files.py +102 -0
- package/latticeai/api/memory.py +128 -1
- package/latticeai/api/portability.py +39 -4
- package/latticeai/api/review_queue.py +126 -0
- package/latticeai/api/search.py +16 -2
- package/latticeai/core/agent.py +59 -2
- package/latticeai/core/agent_prompts.py +66 -0
- package/latticeai/core/config.py +4 -1
- package/latticeai/core/context_builder.py +98 -11
- package/latticeai/core/embedding_providers.py +528 -0
- package/latticeai/core/legacy_compatibility.py +1 -1
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/messages.py +180 -0
- package/latticeai/core/model_compat.py +73 -2
- package/latticeai/core/workspace_os.py +43 -0
- package/latticeai/core/workspace_os_constants.py +1 -1
- package/latticeai/core/workspace_reorganization.py +335 -0
- package/latticeai/models/model_providers.py +12 -4
- package/latticeai/runtime/build_phases.py +33 -2
- package/latticeai/runtime/chat_wiring.py +4 -0
- package/latticeai/runtime/feature_toggle_wiring.py +163 -0
- package/latticeai/runtime/persistence_runtime.py +41 -4
- package/latticeai/runtime/router_registration.py +11 -0
- package/latticeai/runtime/runtime_context.py +1 -0
- package/latticeai/services/app_context.py +8 -0
- package/latticeai/services/architecture_readiness.py +1 -1
- package/latticeai/services/automation_intelligence.py +22 -2
- package/latticeai/services/brain_intelligence.py +123 -7
- package/latticeai/services/change_proposals.py +50 -10
- package/latticeai/services/command_center.py +10 -4
- package/latticeai/services/feature_toggles.py +502 -0
- package/latticeai/services/folder_watch.py +122 -1
- package/latticeai/services/hybrid_chat.py +56 -5
- package/latticeai/services/interop_bridges.py +978 -0
- package/latticeai/services/memory_service.py +34 -0
- package/latticeai/services/model_capability_registry.py +434 -261
- package/latticeai/services/model_catalog.py +95 -61
- package/latticeai/services/model_recommendation.py +18 -11
- package/latticeai/services/model_runtime.py +1 -1
- package/latticeai/services/multimodal_ports.py +112 -0
- package/latticeai/services/obsidian_bridge.py +16 -25
- package/latticeai/services/product_readiness.py +1 -1
- package/latticeai/services/search_service.py +149 -2
- package/latticeai/services/self_model_service.py +171 -0
- package/latticeai/services/tool_dispatch.py +4 -0
- package/latticeai/services/voice_capture.py +27 -1
- package/latticeai/setup/auto_setup.py +27 -30
- package/latticeai/setup/wizard.py +77 -44
- package/package.json +1 -1
- package/scripts/check_current_release_docs.mjs +1 -1
- package/scripts/check_server_i18n.mjs +1 -0
- package/scripts/release_screen_claims.json +22 -0
- package/scripts/verify_hf_model_registry.py +253 -218
- 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 +37 -37
- package/static/app/assets/{Act-D4zSxFR-.js → Act-AWf0SAKp.js} +1 -1
- package/static/app/assets/{AdminConsole-w5jBfPt2.js → AdminConsole-D0u8Tiyj.js} +1 -1
- package/static/app/assets/{Brain-C2EqQg74.js → Brain-tuhI4sOC.js} +1 -1
- package/static/app/assets/BrainHome-Ts7G_Ila.js +2 -0
- package/static/app/assets/BrainSignals-jMYgQ2Ar.js +1 -0
- package/static/app/assets/{Capture-DPqpGK8d.js → Capture-CqOSzyPr.js} +1 -1
- package/static/app/assets/{CommandPalette-CNf7h5fp.js → CommandPalette-DC0Bzh-I.js} +1 -1
- package/static/app/assets/{Library-BN0HYOfc.js → Library-CX-bbhmK.js} +1 -1
- package/static/app/assets/{LivingBrain-Dfq_wEDI.js → LivingBrain-DBwhto14.js} +1 -1
- package/static/app/assets/{ProductFlow-B-3O0rNV.js → ProductFlow-BHA2cfKI.js} +1 -1
- package/static/app/assets/{ReviewCard-gZ-tdqFM.js → ReviewCard-BUhCKRNM.js} +1 -1
- package/static/app/assets/{System-BElUcSSw.js → System-Bu2t5hn1.js} +1 -1
- package/static/app/assets/arrow-left-Dzwa5zRb.js +1 -0
- package/static/app/assets/{bot--qYHMtkP.js → bot-Cia42c2h.js} +1 -1
- package/static/app/assets/brain-DJMoqrwx.js +1 -0
- package/static/app/assets/{button-51Z3rsuv.js → button-2j2Ijzgq.js} +1 -1
- package/static/app/assets/{circle-pause-CMIiMaQl.js → circle-pause-BEFeWpVW.js} +1 -1
- package/static/app/assets/{circle-play-DZoO_cfG.js → circle-play-ujXMcHxl.js} +1 -1
- package/static/app/assets/{cpu-Bs6uc9W9.js → cpu-k4awryFq.js} +1 -1
- package/static/app/assets/{download-G-2olkWz.js → download-DFbLJ_ig.js} +1 -1
- package/static/app/assets/{folder-open-CTOspnmb.js → folder-open-7y_b6xkM.js} +1 -1
- package/static/app/assets/{hard-drive-CewHWJhn.js → hard-drive-Bidh02Kr.js} +1 -1
- package/static/app/assets/{index-D7Rr-J2Y.js → index-BpYkzcVm.js} +3 -3
- package/static/app/assets/index-DwDl9-8Y.css +2 -0
- package/static/app/assets/{input-D4w_BZWl.js → input-DSlJJxRs.js} +1 -1
- package/static/app/assets/{permissionCopy-CosBEXAZ.js → permissionCopy-Bpb83Hx9.js} +1 -1
- package/static/app/assets/{primitives-d0g9pvzS.js → primitives-BCx6TvfG.js} +1 -1
- package/static/app/assets/search-Cgy8cCFJ.js +1 -0
- package/static/app/assets/{share-2-NmD7e_oV.js → share-2-BH1M-WNi.js} +1 -1
- package/static/app/assets/{shield-alert-CcQeMuju.js → shield-alert-BlKdBXcG.js} +1 -1
- package/static/app/assets/{textarea-BPAJDc-0.js → textarea-CCWbUfFB.js} +1 -1
- package/static/app/assets/{useFocusTrap-C7YLdTBC.js → useFocusTrap-YdHQ7pJ1.js} +1 -1
- package/static/app/assets/{useQuery-DRyD9opW.js → useQuery-CXQiwbVT.js} +1 -1
- package/static/app/assets/{utils-DG1_ExrP.js → utils-zqPZJxdx.js} +2 -2
- package/static/app/assets/{workspace-CWVf3gsI.js → workspace-DXTihhfU.js} +1 -1
- package/static/app/index.html +4 -4
- package/static/sw.js +1 -1
- package/static/app/assets/BrainHome-CvXS6XiQ.js +0 -2
- package/static/app/assets/BrainSignals-DOE_KhOU.js +0 -1
- package/static/app/assets/arrow-left-CFNIMjhv.js +0 -1
- package/static/app/assets/brain-DDCLjRqO.js +0 -1
- package/static/app/assets/index-CkzokZAj.css +0 -2
- package/static/app/assets/search-BLCYt75v.js +0 -1
|
@@ -26,7 +26,8 @@ import re
|
|
|
26
26
|
from typing import Any, Dict, List
|
|
27
27
|
|
|
28
28
|
from lattice_brain.context import approx_tokens
|
|
29
|
-
from lattice_brain.graph.retrieval import context_quality_signal
|
|
29
|
+
from lattice_brain.graph.retrieval import context_quality_signal, multimodal_signal
|
|
30
|
+
from lattice_brain.self_model import DEFAULT_SUMMARY_TOKENS, summary_for_prompt
|
|
30
31
|
|
|
31
32
|
_CLEAN_RE = re.compile(r"\s+")
|
|
32
33
|
|
|
@@ -35,6 +36,20 @@ _CLEAN_RE = re.compile(r"\s+")
|
|
|
35
36
|
# other on the same Brain.
|
|
36
37
|
DEFAULT_DOCUMENT_CONTEXT_BUDGET = 2000
|
|
37
38
|
|
|
39
|
+
# ── Self-Model injection (v11.1.0) ──────────────────────────────────────────
|
|
40
|
+
# What the Brain knows about its owner rides along with the knowledge, so a
|
|
41
|
+
# generated document sounds like the person who asked for it. Three rules keep
|
|
42
|
+
# it from becoming another thing that can go wrong:
|
|
43
|
+
# 1. an empty Self-Model injects *nothing* — no header, no blank section;
|
|
44
|
+
# 2. the block never takes more than half the context budget, so the profile
|
|
45
|
+
# can never crowd out the knowledge the request is actually about;
|
|
46
|
+
# 3. the returned contract is unchanged — same keys, same
|
|
47
|
+
# ``context_quality`` shape, one extra trace section only when a block
|
|
48
|
+
# was really injected.
|
|
49
|
+
SELF_MODEL_SECTION_TITLE = "사용자 프로필"
|
|
50
|
+
SELF_MODEL_TRACE_SOURCE = "self_model"
|
|
51
|
+
SELF_MODEL_SECTION_HEADER = f"### 🙋 {SELF_MODEL_SECTION_TITLE}\n\n"
|
|
52
|
+
|
|
38
53
|
|
|
39
54
|
def _clean(text: str, max_len: int = 700) -> str:
|
|
40
55
|
return _CLEAN_RE.sub(" ", str(text or "")).strip()[:max_len]
|
|
@@ -72,6 +87,55 @@ def _fit_to_budget(context_md: str, budget: int) -> tuple:
|
|
|
72
87
|
return head.rstrip(), True
|
|
73
88
|
|
|
74
89
|
|
|
90
|
+
def _self_model_budget(budget: int, limit_tokens: int) -> int:
|
|
91
|
+
"""Tokens the profile block may use — never more than half the budget."""
|
|
92
|
+
if budget <= 0:
|
|
93
|
+
return limit_tokens
|
|
94
|
+
return min(limit_tokens, budget // 2)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _self_model_block(
|
|
98
|
+
kg_store,
|
|
99
|
+
*,
|
|
100
|
+
enabled: bool,
|
|
101
|
+
budget: int,
|
|
102
|
+
limit_tokens: int,
|
|
103
|
+
allowed_workspaces,
|
|
104
|
+
) -> str:
|
|
105
|
+
"""Rendered profile section, or ``""`` when there is nothing to inject.
|
|
106
|
+
|
|
107
|
+
The section header is charged to the same allowance as the summary, so the
|
|
108
|
+
*rendered block* — not just its text — honours the ceiling.
|
|
109
|
+
"""
|
|
110
|
+
allowance = _self_model_budget(budget, limit_tokens) - approx_tokens(
|
|
111
|
+
SELF_MODEL_SECTION_HEADER
|
|
112
|
+
)
|
|
113
|
+
if not enabled or allowance <= 0:
|
|
114
|
+
return ""
|
|
115
|
+
summary = summary_for_prompt(
|
|
116
|
+
kg_store, limit_tokens=allowance, allowed_workspaces=allowed_workspaces
|
|
117
|
+
)
|
|
118
|
+
if not summary:
|
|
119
|
+
return ""
|
|
120
|
+
return f"{SELF_MODEL_SECTION_HEADER}{summary}"
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _with_self_model(block: str, context_md: str) -> str:
|
|
124
|
+
"""Prepend the profile block to rendered knowledge (either may be empty)."""
|
|
125
|
+
return "\n\n".join(part for part in (block, context_md) if part)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _self_model_trace(block: str) -> List[Dict[str, Any]]:
|
|
129
|
+
if not block:
|
|
130
|
+
return []
|
|
131
|
+
return [{
|
|
132
|
+
"name": SELF_MODEL_SECTION_TITLE,
|
|
133
|
+
"source": SELF_MODEL_TRACE_SOURCE,
|
|
134
|
+
"approx_tokens": approx_tokens(block),
|
|
135
|
+
"provenance": [],
|
|
136
|
+
}]
|
|
137
|
+
|
|
138
|
+
|
|
75
139
|
def retrieve_context_for_generation(
|
|
76
140
|
kg_store,
|
|
77
141
|
query: str,
|
|
@@ -81,6 +145,8 @@ def retrieve_context_for_generation(
|
|
|
81
145
|
allowed_workspaces=None,
|
|
82
146
|
include_legacy_global: bool = False,
|
|
83
147
|
budget: int = DEFAULT_DOCUMENT_CONTEXT_BUDGET,
|
|
148
|
+
include_self_model: bool = True,
|
|
149
|
+
self_model_tokens: int = DEFAULT_SUMMARY_TOKENS,
|
|
84
150
|
) -> Dict[str, Any]:
|
|
85
151
|
"""Knowledge Graph에서 문서 생성에 필요한 컨텍스트를 검색·조합한다.
|
|
86
152
|
|
|
@@ -106,6 +172,21 @@ def retrieve_context_for_generation(
|
|
|
106
172
|
if allowed_workspaces is not None
|
|
107
173
|
else {}
|
|
108
174
|
)
|
|
175
|
+
self_model_md = _self_model_block(
|
|
176
|
+
kg_store,
|
|
177
|
+
enabled=include_self_model,
|
|
178
|
+
budget=budget,
|
|
179
|
+
limit_tokens=self_model_tokens,
|
|
180
|
+
allowed_workspaces=allowed_workspaces,
|
|
181
|
+
)
|
|
182
|
+
# The profile's tokens come out of the same budget, so injecting it can
|
|
183
|
+
# never push the assembled context over the ceiling the caller asked for.
|
|
184
|
+
# The blank line that joins the two blocks is counted too — otherwise the
|
|
185
|
+
# assembled total lands one token over a tight budget.
|
|
186
|
+
knowledge_budget = budget
|
|
187
|
+
if self_model_md:
|
|
188
|
+
knowledge_budget -= approx_tokens(f"{self_model_md}\n\n")
|
|
189
|
+
|
|
109
190
|
results = kg_store.search_for_document_generation(query, limit=max_results, **scope_kwargs)
|
|
110
191
|
if not results:
|
|
111
192
|
fallback_ctx = kg_store.context_for_query(
|
|
@@ -113,7 +194,7 @@ def retrieve_context_for_generation(
|
|
|
113
194
|
limit=max_results,
|
|
114
195
|
**scope_kwargs,
|
|
115
196
|
)
|
|
116
|
-
fallback_ctx, trimmed = _fit_to_budget(fallback_ctx or "",
|
|
197
|
+
fallback_ctx, trimmed = _fit_to_budget(fallback_ctx or "", knowledge_budget)
|
|
117
198
|
# Lexical fallback: the hybrid document search found nothing, so the
|
|
118
199
|
# signal says lexical_only — exactly what chat reports in the same
|
|
119
200
|
# situation, never a quiet downgrade.
|
|
@@ -121,16 +202,17 @@ def retrieve_context_for_generation(
|
|
|
121
202
|
"lexical_only" if fallback_ctx else "none",
|
|
122
203
|
1 if fallback_ctx else 0,
|
|
123
204
|
)
|
|
205
|
+
assembled = _with_self_model(self_model_md, fallback_ctx)
|
|
124
206
|
return {
|
|
125
207
|
"query": query,
|
|
126
|
-
"context_markdown":
|
|
208
|
+
"context_markdown": assembled,
|
|
127
209
|
"sources": [],
|
|
128
210
|
"stats": {"method": "fallback", "matches": 0, "budget_trimmed": trimmed},
|
|
129
211
|
"context_quality": quality,
|
|
130
212
|
"trace": {
|
|
131
213
|
"budget_approx_tokens": budget,
|
|
132
|
-
"used_approx_tokens": approx_tokens(
|
|
133
|
-
"sections": [{
|
|
214
|
+
"used_approx_tokens": approx_tokens(assembled),
|
|
215
|
+
"sections": _self_model_trace(self_model_md) + [{
|
|
134
216
|
"name": "Knowledge (fallback)",
|
|
135
217
|
"source": "knowledge",
|
|
136
218
|
"approx_tokens": approx_tokens(fallback_ctx),
|
|
@@ -154,11 +236,12 @@ def retrieve_context_for_generation(
|
|
|
154
236
|
sections = _build_context_sections(results, extra_nodes_by_id, hop_data.get("edges", []))
|
|
155
237
|
context_md = _render_markdown(query, sections)
|
|
156
238
|
sources = _extract_sources(results)
|
|
157
|
-
context_md, trimmed = _fit_to_budget(context_md,
|
|
239
|
+
context_md, trimmed = _fit_to_budget(context_md, knowledge_budget)
|
|
240
|
+
assembled = _with_self_model(self_model_md, context_md)
|
|
158
241
|
|
|
159
242
|
return {
|
|
160
243
|
"query": query,
|
|
161
|
-
"context_markdown":
|
|
244
|
+
"context_markdown": assembled,
|
|
162
245
|
"sources": sources,
|
|
163
246
|
"stats": {
|
|
164
247
|
"method": "hybrid",
|
|
@@ -167,12 +250,15 @@ def retrieve_context_for_generation(
|
|
|
167
250
|
"graph_edges": len(hop_data.get("edges", [])),
|
|
168
251
|
"budget_trimmed": trimmed,
|
|
169
252
|
},
|
|
170
|
-
# Same honest signal chat reports for the same Brain
|
|
171
|
-
|
|
253
|
+
# Same honest signal chat reports for the same Brain — including the
|
|
254
|
+
# multimodal key when the document's context really rests on pictures.
|
|
255
|
+
"context_quality": context_quality_signal(
|
|
256
|
+
"hybrid", len(results), multimodal=multimodal_signal(results)
|
|
257
|
+
),
|
|
172
258
|
"trace": {
|
|
173
259
|
"budget_approx_tokens": budget,
|
|
174
|
-
"used_approx_tokens": approx_tokens(
|
|
175
|
-
"sections": [
|
|
260
|
+
"used_approx_tokens": approx_tokens(assembled),
|
|
261
|
+
"sections": _self_model_trace(self_model_md) + [
|
|
176
262
|
{
|
|
177
263
|
"name": section["title"],
|
|
178
264
|
"source": "knowledge",
|
|
@@ -199,6 +285,7 @@ def _build_context_sections(
|
|
|
199
285
|
|
|
200
286
|
docs = [r for r in primary_results if r["type"] in (
|
|
201
287
|
"Document", "File", "SlideDeck", "Spreadsheet", "CodeFile", "Image", "ImageText",
|
|
288
|
+
"Audio",
|
|
202
289
|
)]
|
|
203
290
|
if docs:
|
|
204
291
|
sections.append({
|