ltcai 3.2.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -67
- package/docs/CHANGELOG.md +36 -0
- package/docs/architecture.md +2 -1
- package/docs/assets/v3.4.0/agent-run.png +0 -0
- package/docs/assets/v3.4.0/agents.png +0 -0
- package/docs/assets/v3.4.0/before/chat-before.png +0 -0
- package/docs/assets/v3.4.0/before/files-before.png +0 -0
- package/docs/assets/v3.4.0/chat.png +0 -0
- package/docs/assets/v3.4.0/connect-folder.png +0 -0
- package/docs/assets/v3.4.0/files.png +0 -0
- package/docs/assets/v3.4.0/home.png +0 -0
- package/docs/assets/v3.4.0/hooks-dispatch.png +0 -0
- package/docs/assets/v3.4.0/knowledge-graph.png +0 -0
- package/docs/assets/v3.4.0/local-agent.png +0 -0
- package/docs/assets/v3.4.0/memory.png +0 -0
- package/docs/assets/v3.4.0/settings.png +0 -0
- package/docs/assets/v3.4.0/vision-input.png +0 -0
- package/docs/assets/v3.4.0/workflows.png +0 -0
- package/knowledge_graph.py +45 -0
- package/knowledge_graph_api.py +10 -0
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/agents.py +3 -0
- package/latticeai/api/hooks.py +39 -0
- package/latticeai/api/local_files.py +41 -0
- package/latticeai/api/models.py +36 -1
- package/latticeai/api/tools.py +16 -1
- package/latticeai/api/workflow_designer.py +2 -1
- package/latticeai/core/hooks.py +398 -2
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/multi_agent.py +1 -1
- package/latticeai/core/workflow_engine.py +21 -1
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/server_app.py +40 -0
- package/latticeai/services/agent_runtime.py +46 -1
- package/latticeai/services/upload_service.py +17 -0
- package/package.json +1 -1
- package/scripts/build_v3_assets.mjs +7 -1
- package/scripts/capture/capture_v340.js +88 -0
- package/static/css/{tokens.5a595671.css → tokens.3ba22e37.css} +109 -109
- package/static/css/tokens.css +109 -109
- package/static/v3/asset-manifest.json +25 -25
- package/static/v3/css/{lattice.components.011e988b.css → lattice.components.9b49d614.css} +57 -32
- package/static/v3/css/lattice.components.css +57 -32
- package/static/v3/css/{lattice.shell.4920f42d.css → lattice.shell.6ceea7c8.css} +75 -31
- package/static/v3/css/lattice.shell.css +75 -31
- package/static/v3/css/lattice.tokens.css +13 -13
- package/static/v3/css/{lattice.tokens.c597ff81.css → lattice.tokens.e7018963.css} +13 -13
- package/static/v3/css/{lattice.views.3ee19d4e.css → lattice.views.22f69117.css} +98 -15
- package/static/v3/css/lattice.views.css +98 -15
- package/static/v3/js/{app.a5adc0f3.js → app.c4acfdd8.js} +1 -1
- package/static/v3/js/core/{api.603b978f.js → api.12b568ad.js} +126 -4
- package/static/v3/js/core/api.js +126 -4
- package/static/v3/js/core/{components.4c83e0a9.js → components.35f02e4c.js} +8 -0
- package/static/v3/js/core/components.js +8 -0
- package/static/v3/js/core/{routes.07ad6696.js → routes.d214b399.js} +16 -12
- package/static/v3/js/core/routes.js +16 -12
- package/static/v3/js/core/{shell.ea0b9ae5.js → shell.80a6ad82.js} +37 -9
- package/static/v3/js/core/shell.js +34 -6
- package/static/v3/js/views/agents.014d0b74.js +541 -0
- package/static/v3/js/views/agents.js +305 -57
- package/static/v3/js/views/{chat.718144ce.js → chat.e6dd7dd0.js} +162 -10
- package/static/v3/js/views/chat.js +162 -10
- package/static/v3/js/views/files.adad14c1.js +365 -0
- package/static/v3/js/views/files.js +269 -90
- package/static/v3/js/views/home.24f8b8ae.js +200 -0
- package/static/v3/js/views/home.js +96 -15
- package/static/v3/js/views/hooks.13845954.js +215 -0
- package/static/v3/js/views/hooks.js +117 -1
- package/static/v3/js/views/{memory.d2ed7a7c.js → memory.4ebdf474.js} +5 -4
- package/static/v3/js/views/memory.js +5 -4
- package/static/v3/js/views/{my-computer.1b2ff621.js → my-computer.c3ef5283.js} +224 -1
- package/static/v3/js/views/my-computer.js +224 -1
- package/static/v3/js/views/{settings.4f777210.js → settings.8631fa5e.js} +70 -2
- package/static/v3/js/views/settings.js +70 -2
- package/static/v3/js/views/agents.c373d48c.js +0 -293
- package/static/v3/js/views/files.4935197e.js +0 -186
- package/static/v3/js/views/home.cdde3b32.js +0 -119
- package/static/v3/js/views/hooks.f3edebca.js +0 -99
package/README.md
CHANGED
|
@@ -16,14 +16,14 @@
|
|
|
16
16
|
[](https://www.npmjs.com/package/ltcai)
|
|
17
17
|
[](https://marketplace.visualstudio.com/items?itemName=parktaesoo.ltcai)
|
|
18
18
|
[](https://open-vsx.org/extension/parktaesoo/ltcai)
|
|
19
|
-
[](https://github.com/TaeSooPark-PTS/LatticeAI/releases
|
|
19
|
+
[](https://github.com/TaeSooPark-PTS/LatticeAI/releases)
|
|
20
20
|
[](LICENSE)
|
|
21
21
|
[](https://www.python.org/)
|
|
22
22
|
[](https://marketplace.visualstudio.com/items?itemName=parktaesoo.ltcai)
|
|
23
23
|
|
|
24
24
|
</div>
|
|
25
25
|
|
|
26
|
-

|
|
27
27
|
|
|
28
28
|
## Install
|
|
29
29
|
|
|
@@ -49,7 +49,7 @@ Install the coding extension:
|
|
|
49
49
|
|
|
50
50
|
- [VS Code Marketplace: parktaesoo.ltcai](https://marketplace.visualstudio.com/items?itemName=parktaesoo.ltcai)
|
|
51
51
|
- [Open VSX: parktaesoo.ltcai](https://open-vsx.org/extension/parktaesoo/ltcai)
|
|
52
|
-
- [GitHub
|
|
52
|
+
- [GitHub Releases](https://github.com/TaeSooPark-PTS/LatticeAI/releases)
|
|
53
53
|
|
|
54
54
|
## Quick Start
|
|
55
55
|
|
|
@@ -86,9 +86,9 @@ Lattice AI v3 is a local-first AI workspace platform for people and teams who
|
|
|
86
86
|
want their files, models, graph context, retrieval, and agent workflows in one
|
|
87
87
|
place.
|
|
88
88
|
|
|
89
|
-
- **Primary app shell**: `/app` is the default product experience with
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
- **Primary app shell**: `/app` is the default product experience with Chat,
|
|
90
|
+
Files, Hybrid Search, Knowledge Graph, Memory, Models, Settings, Advanced
|
|
91
|
+
agent/workflow tooling, and Admin areas. Classic pages remain compatibility
|
|
92
92
|
routes only; normal workflows stay in `/app`.
|
|
93
93
|
- **Local-first AI Workspace**: work starts on your machine, with local data and
|
|
94
94
|
workspace state by default.
|
|
@@ -122,79 +122,96 @@ and disconnected automations. Lattice AI keeps those parts together:
|
|
|
122
122
|
- multi-agent workflows leave behind replayable plans, reviews, retries, and
|
|
123
123
|
outcomes.
|
|
124
124
|
|
|
125
|
-
## v3.
|
|
126
|
-
|
|
127
|
-
Lattice AI v3.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
- **
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
-
|
|
142
|
-
|
|
143
|
-
- **
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
- **
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
125
|
+
## v3.4.0 Highlights
|
|
126
|
+
|
|
127
|
+
Lattice AI v3.4.0 is the **platform completion** release: it closes the remaining
|
|
128
|
+
non-enterprise functionality gaps the v3.3.0 honesty audit flagged, so the
|
|
129
|
+
local-first workspace is complete and demonstrable end-to-end. Each item below is
|
|
130
|
+
runtime-verified on a live server, not only wired in source.
|
|
131
|
+
|
|
132
|
+
- **Hooks now execute.** A real dispatch engine (`run_hook` / `run_hooks` /
|
|
133
|
+
`fire_hook` + `HookContext` / `HookResult`) runs hooks at genuine lifecycle
|
|
134
|
+
points — agents (pre/post-run), workflows (start/end), tools (pre/post-tool),
|
|
135
|
+
and the upload pipeline. `pre_*` hooks can gate (block) an action; every
|
|
136
|
+
dispatch is recorded to a persisted run log surfaced in the Hooks view.
|
|
137
|
+
- **Uploads appear in Files.** Uploaded documents are listed with live ingest →
|
|
138
|
+
index state (`/knowledge-graph/documents`), completing upload → Files →
|
|
139
|
+
Knowledge Graph → Hybrid Search → Chat.
|
|
140
|
+
- **Vision (VLM) image input.** The Chat composer accepts images by attach,
|
|
141
|
+
drag-and-drop, or paste, with a preview and a **Vision Enabled / Disabled**
|
|
142
|
+
badge driven by the active model's capability.
|
|
143
|
+
- **Run agents from the Agents view.** A Run console (goal + roles → Run / Stop /
|
|
144
|
+
Status / Queue / Logs) executes the multi-agent pipeline locally; it runs
|
|
145
|
+
without a model and fires its pre/post-run hooks.
|
|
146
|
+
- **On-device Local Agent + Connect Folder + Folder Watch.** My Computer reports
|
|
147
|
+
the real local-runtime agent status and handshake; folders can be connected and
|
|
148
|
+
watched (debounced reindex on change) through the existing on-device endpoints.
|
|
149
|
+
- **Enterprise stays honestly disabled.** SSO, SCIM, DLP, Private VPC, SIEM, and
|
|
150
|
+
enterprise RBAC remain off with honest "not available in this build" states.
|
|
151
|
+
|
|
152
|
+
See [RELEASE_NOTES_v3.4.0.md](RELEASE_NOTES_v3.4.0.md),
|
|
153
|
+
[PLATFORM_COMPLETION_REPORT_v3.4.0.md](PLATFORM_COMPLETION_REPORT_v3.4.0.md), and
|
|
154
|
+
the evidence-traced [FEATURE_STATUS.md](FEATURE_STATUS.md).
|
|
155
|
+
|
|
156
|
+
## v3.3.1 Highlights
|
|
157
|
+
|
|
158
|
+
Lattice AI v3.3.1 rebuilds the visible `/app` product experience while
|
|
159
|
+
preserving the existing local-first runtime. The app now presents Chat, Files,
|
|
160
|
+
Search, Knowledge, Memory, Models, Settings, Advanced tooling, and Admin
|
|
161
|
+
workflows with clearer navigation and honest live/unavailable states.
|
|
162
|
+
|
|
163
|
+
- **Visual product rebuild** — compact rail navigation, quieter topbar,
|
|
164
|
+
command-palette search, retrieval readiness footer, and denser controls.
|
|
165
|
+
- **Truthful Home dashboard** — backend, model, retrieval, memory, source, and
|
|
166
|
+
trace readiness are derived from real endpoints instead of fabricated counts.
|
|
167
|
+
- **Basic / Advanced / Admin navigation** — Basic focuses on core workspace
|
|
168
|
+
workflows; Advanced exposes agents, workflows, skills, hooks, and MCP; Admin
|
|
169
|
+
keeps organization controls separate.
|
|
170
|
+
- **Files and Settings clarity** — manual upload is available immediately,
|
|
171
|
+
folder watching is explicitly tied to the desktop local agent, and Settings
|
|
172
|
+
shows backend, agent, model, telemetry, and embedding readiness.
|
|
173
|
+
- **Design system refresh** — cooler neutral light/dark tokens, tighter 8px
|
|
174
|
+
radius discipline, compact cards/tables/stats/buttons, and regenerated
|
|
175
|
+
hashed v3 assets.
|
|
176
|
+
|
|
177
|
+
The v3.2.0 platform remains the feature-complete foundation: multi-agent
|
|
178
|
+
collaboration, Agent Registry, Marketplace templates, Workflow Agents,
|
|
179
|
+
Autonomous Planning, Long-Term Memory, Skills, Hooks, Tool Registry, MCP
|
|
180
|
+
Manager, production embedding profiles, and hash-manifested `/app` assets.
|
|
181
|
+
Release audit: [docs/V3_2_AUDIT.md](docs/V3_2_AUDIT.md).
|
|
174
182
|
|
|
175
183
|
## Screenshots
|
|
176
184
|
|
|
177
|
-
|
|
185
|
+
All screenshots are the v3.4.0 `/app` shell. Live model output (VLM inference,
|
|
186
|
+
agent-generated text) requires a loaded local model and is not depicted.
|
|
178
187
|
|
|
179
|
-
|
|
188
|
+
### Home
|
|
180
189
|
|
|
181
|
-

|
|
182
191
|
|
|
183
|
-
###
|
|
192
|
+
### Chat with Vision (VLM) image input
|
|
193
|
+
|
|
194
|
+

|
|
195
|
+
|
|
196
|
+
### Files — uploaded documents + Connect Folder
|
|
184
197
|
|
|
185
|
-

|
|
186
199
|
|
|
187
|
-
###
|
|
200
|
+
### Run agents from the Agents view
|
|
188
201
|
|
|
189
|
-

|
|
190
203
|
|
|
191
|
-
###
|
|
204
|
+
### Hooks dispatch + run log
|
|
192
205
|
|
|
193
|
-

|
|
194
207
|
|
|
195
|
-
###
|
|
208
|
+
### Local Agent (on-device runtime)
|
|
209
|
+
|
|
210
|
+

|
|
211
|
+
|
|
212
|
+
### Knowledge Graph
|
|
196
213
|
|
|
197
|
-

|
|
198
215
|
|
|
199
216
|
## Knowledge Graph Flow
|
|
200
217
|
|
|
@@ -290,7 +307,10 @@ Core areas:
|
|
|
290
307
|
|
|
291
308
|
| Version | Theme |
|
|
292
309
|
| --- | --- |
|
|
293
|
-
| **3.
|
|
310
|
+
| **3.4.0** | Platform completion — hooks execution engine, uploads visible in Files, VLM image input, agent run trigger, on-device Local Agent / Connect Folder / Folder Watch; Enterprise stays honestly disabled; refreshed v3.4.0 public assets |
|
|
311
|
+
| 3.3.1 | Visual product rebuild — rebuilt `/app` shell, Basic/Advanced/Admin navigation, cooler token palette, compact component system, Home readiness dashboard, Files local-agent truthfulness, Settings runtime status, and v3.3.1 design notes |
|
|
312
|
+
| **3.3.0** | Product quality & honesty release — evidence-based feature audit (`FEATURE_STATUS.md`), single-source version truth, working manual document upload in Files, fixed document-generation streaming, truthful Home retrieval status, documented design system (`STYLE_SYSTEM.md`) |
|
|
313
|
+
| 3.2.0 | Feature-complete platform — multi-agent collaboration, agent registry, marketplace + templates, workflow agents, autonomous planning, long-term memory + manager, skills/hooks/tool registries, MCP manager, all operable from `/app` |
|
|
294
314
|
| 3.1.0 | Mainline platform completion — native `/app` workflows, Classic retired from normal paths, production embedding profiles, AgentRuntime/registries, hashed v3 assets |
|
|
295
315
|
| 3.0.1 | Release-blocker remediation — provider-backed embeddings (Hash/MLX/Ollama/OpenAI/Custom), unified AgentRuntime boundary, every v3 surface connected or clearly unavailable |
|
|
296
316
|
| 3.0.0 | v3 local-first AI workspace platform — `/app`, Native Chat, Knowledge Graph, Vector Index, Hybrid Search, workspace modes |
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [3.3.1] - 2026-06-08
|
|
4
|
+
|
|
5
|
+
> v3.3.1 — Visual Product Rebuild. The `/app` frontend keeps the same runtime
|
|
6
|
+
> behavior but receives a new product shell, navigation hierarchy, visual token
|
|
7
|
+
> system, and readiness-focused primary views.
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Rebuilt the global `/app` shell with a denser command rail, grouped
|
|
12
|
+
Basic/Advanced/Admin navigation, local retrieval readiness footer, quiet
|
|
13
|
+
topbar, and mode-aware command palette.
|
|
14
|
+
- Reorganized production navigation to Basic (Home, Chat, Files, Search,
|
|
15
|
+
Knowledge, Memory, Models, Settings), Advanced (Agents, Workflows, Skills,
|
|
16
|
+
Hooks, MCP), and Admin (Users, Permissions, Audit Logs, Security, Policies,
|
|
17
|
+
Private VPC). Compatibility-only views remain deep-linkable.
|
|
18
|
+
- Replaced the v3.3.0 palette with cooler neutral light/dark tokens, tightened
|
|
19
|
+
cards/panels to an 8px radius system, and compacted buttons, inputs, stats,
|
|
20
|
+
tables, segmented controls, and empty states.
|
|
21
|
+
- Rebuilt Home as a local readiness dashboard for backend, model, retrieval,
|
|
22
|
+
memory, connected sources, stats, and recent activity when available.
|
|
23
|
+
- Clarified Files manual upload versus desktop local-agent folder connection.
|
|
24
|
+
- Added Settings runtime readiness for backend, desktop local agent, model
|
|
25
|
+
runtime, host telemetry, and embedding provider configuration.
|
|
26
|
+
- Fixed Chat send/stop button wiring so streaming uses a stable handler.
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- `VISUAL_REBUILD_NOTES_v3.3.1.md` with implementation and QA notes.
|
|
31
|
+
- `FIGMA_SPEC.md` as the in-repo Figma-equivalent design spec for v3.3.1.
|
|
32
|
+
|
|
33
|
+
### Validation
|
|
34
|
+
|
|
35
|
+
- `npm run build:assets` regenerated content-hashed v3 assets at `3.3.1`.
|
|
36
|
+
- Package publication, deployment, tags, and GitHub Release creation were not
|
|
37
|
+
performed.
|
|
38
|
+
|
|
3
39
|
## [3.2.0] - 2026-06-08
|
|
4
40
|
|
|
5
41
|
> v3.2 — Feature-Complete Platform. Multi-agent collaboration, an agent
|
package/docs/architecture.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Lattice AI Architecture
|
|
2
2
|
|
|
3
|
-
> v3.
|
|
3
|
+
> v3.3.1 — feature-complete for non-enterprise use cases with a rebuilt `/app`
|
|
4
|
+
> visual shell. The agent ecosystem
|
|
4
5
|
> (registry, marketplace + templates, workflow agents, autonomous planning),
|
|
5
6
|
> the long-term memory platform + manager, and the skills/hooks/tool/MCP
|
|
6
7
|
> registries are all operable from `/app`. Enterprise controls remain future
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/knowledge_graph.py
CHANGED
|
@@ -3046,6 +3046,51 @@ class KnowledgeGraphStore:
|
|
|
3046
3046
|
"Decision", # 결정 사항
|
|
3047
3047
|
)
|
|
3048
3048
|
|
|
3049
|
+
def list_documents(self, limit: int = 200) -> Dict[str, Any]:
|
|
3050
|
+
"""List ingested ``Document`` nodes with their ingest + index state.
|
|
3051
|
+
|
|
3052
|
+
Powers the Files view: every accepted upload and every indexed local
|
|
3053
|
+
document becomes a ``Document`` node. A document is reported ``indexed``
|
|
3054
|
+
once its retrieval chunks exist (searchable in Chat / Hybrid Search).
|
|
3055
|
+
"""
|
|
3056
|
+
limit = max(1, min(int(limit or 200), 1000))
|
|
3057
|
+
nt, _ = self._read_tables()
|
|
3058
|
+
documents: List[Dict[str, Any]] = []
|
|
3059
|
+
with self._connect() as conn:
|
|
3060
|
+
rows = conn.execute(
|
|
3061
|
+
f"SELECT id, title, summary, metadata_json, created_at, updated_at "
|
|
3062
|
+
f"FROM {nt} WHERE type='Document' ORDER BY updated_at DESC, id ASC LIMIT ?",
|
|
3063
|
+
(limit,),
|
|
3064
|
+
).fetchall()
|
|
3065
|
+
for row in rows:
|
|
3066
|
+
meta = _safe_loads(row["metadata_json"]) or {}
|
|
3067
|
+
extracted = meta.get("extracted") or {}
|
|
3068
|
+
node_id = row["id"]
|
|
3069
|
+
chunk_count = conn.execute(
|
|
3070
|
+
f"SELECT COUNT(*) AS c FROM {nt} WHERE type='Chunk' AND metadata_json LIKE ?",
|
|
3071
|
+
(f"%{node_id}%",),
|
|
3072
|
+
).fetchone()["c"]
|
|
3073
|
+
documents.append({
|
|
3074
|
+
"id": node_id,
|
|
3075
|
+
"filename": meta.get("filename") or row["title"],
|
|
3076
|
+
"ext": meta.get("ext"),
|
|
3077
|
+
"mime_type": meta.get("mime_type"),
|
|
3078
|
+
"bytes": meta.get("bytes"),
|
|
3079
|
+
"sha256": meta.get("sha256"),
|
|
3080
|
+
"uploader": meta.get("uploader"),
|
|
3081
|
+
"chars": extracted.get("chars"),
|
|
3082
|
+
"chunks": int(chunk_count or 0),
|
|
3083
|
+
"indexed": int(chunk_count or 0) > 0,
|
|
3084
|
+
"ingest_state": "indexed" if int(chunk_count or 0) > 0 else "ingested",
|
|
3085
|
+
"created_at": row["created_at"],
|
|
3086
|
+
"updated_at": row["updated_at"],
|
|
3087
|
+
})
|
|
3088
|
+
return {
|
|
3089
|
+
"documents": documents,
|
|
3090
|
+
"total": len(documents),
|
|
3091
|
+
"generated_at": datetime.now().isoformat(timespec="seconds"),
|
|
3092
|
+
}
|
|
3093
|
+
|
|
3049
3094
|
def graph(self, limit: int = 300) -> Dict[str, Any]:
|
|
3050
3095
|
limit = max(1, min(int(limit or 300), 2000))
|
|
3051
3096
|
visible = ",".join(f"'{t}'" for t in self._GRAPH_VISIBLE_TYPES)
|
package/knowledge_graph_api.py
CHANGED
|
@@ -75,6 +75,16 @@ def create_knowledge_graph_router(
|
|
|
75
75
|
require_user(request)
|
|
76
76
|
return graph().graph(limit)
|
|
77
77
|
|
|
78
|
+
@router.get("/knowledge-graph/documents")
|
|
79
|
+
async def knowledge_graph_documents(request: Request, limit: int = 200):
|
|
80
|
+
"""Ingested documents (uploads + indexed local docs) with index state.
|
|
81
|
+
|
|
82
|
+
Backs the Files view so uploaded content is visible end-to-end:
|
|
83
|
+
upload → Files → Knowledge Graph → Hybrid Search → Chat.
|
|
84
|
+
"""
|
|
85
|
+
require_user(request)
|
|
86
|
+
return graph().list_documents(limit)
|
|
87
|
+
|
|
78
88
|
@router.get("/knowledge-graph/search")
|
|
79
89
|
async def knowledge_graph_search(q: str, request: Request, limit: int = 30):
|
|
80
90
|
require_user(request)
|
package/latticeai/__init__.py
CHANGED
package/latticeai/api/agents.py
CHANGED
|
@@ -173,5 +173,8 @@ def create_agents_router(
|
|
|
173
173
|
)
|
|
174
174
|
except ValueError as exc:
|
|
175
175
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
176
|
+
except PermissionError as exc:
|
|
177
|
+
# A pre_run hook gated this run (e.g. a policy/permission hook).
|
|
178
|
+
raise HTTPException(status_code=403, detail=str(exc)) from exc
|
|
176
179
|
|
|
177
180
|
return router
|
package/latticeai/api/hooks.py
CHANGED
|
@@ -35,6 +35,13 @@ class HookRegisterRequest(BaseModel):
|
|
|
35
35
|
enabled: bool = True
|
|
36
36
|
|
|
37
37
|
|
|
38
|
+
class HookRunRequest(BaseModel):
|
|
39
|
+
kind: Optional[str] = None
|
|
40
|
+
hook_id: Optional[str] = None
|
|
41
|
+
event: str = ""
|
|
42
|
+
payload: dict = {}
|
|
43
|
+
|
|
44
|
+
|
|
38
45
|
def create_hooks_router(
|
|
39
46
|
*,
|
|
40
47
|
registry: HooksRegistry,
|
|
@@ -48,6 +55,38 @@ def create_hooks_router(
|
|
|
48
55
|
require_user(request)
|
|
49
56
|
return registry.list(kind=kind)
|
|
50
57
|
|
|
58
|
+
# NOTE: declared before the ``/{hook_id:path}`` catch-all so "runs" is not
|
|
59
|
+
# captured as a hook id.
|
|
60
|
+
@router.get("/api/hooks/runs")
|
|
61
|
+
async def hook_runs(request: Request, limit: int = 50, kind: Optional[str] = None):
|
|
62
|
+
require_user(request)
|
|
63
|
+
return registry.recent_runs(limit=limit, kind=kind)
|
|
64
|
+
|
|
65
|
+
@router.post("/api/hooks/run")
|
|
66
|
+
async def run_hooks(req: HookRunRequest, request: Request):
|
|
67
|
+
"""Execute hooks now — by ``kind`` (all enabled hooks of that kind) or a
|
|
68
|
+
single ``hook_id``. Returns the dispatch record so callers can see what
|
|
69
|
+
ran, in what order, and whether the action was blocked."""
|
|
70
|
+
user = require_user(request)
|
|
71
|
+
try:
|
|
72
|
+
if req.hook_id:
|
|
73
|
+
result = registry.run_hook(req.hook_id, event=req.event or None, payload=req.payload, user_email=user)
|
|
74
|
+
elif req.kind:
|
|
75
|
+
result = registry.run_hooks(req.kind, event=req.event or None, payload=req.payload, user_email=user)
|
|
76
|
+
else:
|
|
77
|
+
raise HTTPException(status_code=400, detail="Provide a 'kind' or a 'hook_id' to run.")
|
|
78
|
+
except KeyError as exc:
|
|
79
|
+
raise HTTPException(status_code=404, detail=f"Hook not found: {exc}") from exc
|
|
80
|
+
except ValueError as exc:
|
|
81
|
+
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
82
|
+
append_audit_event("hook_run", user_email=user, hook_id=req.hook_id, kind=req.kind, event=req.event)
|
|
83
|
+
return result
|
|
84
|
+
|
|
85
|
+
# Alias for the same dispatch action (fire == run).
|
|
86
|
+
@router.post("/api/hooks/fire")
|
|
87
|
+
async def fire_hooks(req: HookRunRequest, request: Request):
|
|
88
|
+
return await run_hooks(req, request)
|
|
89
|
+
|
|
51
90
|
@router.get("/api/hooks/{hook_id:path}")
|
|
52
91
|
async def inspect_hook(hook_id: str, request: Request):
|
|
53
92
|
require_user(request)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import platform
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from typing import Optional
|
|
7
8
|
|
|
@@ -39,6 +40,46 @@ def create_local_files_router(
|
|
|
39
40
|
) -> APIRouter:
|
|
40
41
|
router = APIRouter()
|
|
41
42
|
|
|
43
|
+
@router.get("/api/local-agent/status")
|
|
44
|
+
async def local_agent_status(request: Request):
|
|
45
|
+
"""Real on-device runtime status — the 'Local Agent' is the Lattice
|
|
46
|
+
server running on this machine (filesystem access + folder watching +
|
|
47
|
+
local inference), not a separate desktop process. Everything reported
|
|
48
|
+
here is observed, never faked."""
|
|
49
|
+
require_user(request)
|
|
50
|
+
watch = local_kg_watcher.status() if local_kg_watcher else {"available": False, "active": {}}
|
|
51
|
+
sources = []
|
|
52
|
+
try:
|
|
53
|
+
if knowledge_graph is not None:
|
|
54
|
+
sources = (knowledge_graph.local_sources() or {}).get("sources", [])
|
|
55
|
+
except Exception:
|
|
56
|
+
sources = []
|
|
57
|
+
watched = len(watch.get("active", {}) or {})
|
|
58
|
+
return {
|
|
59
|
+
"agent": {
|
|
60
|
+
"id": "lattice-local-runtime",
|
|
61
|
+
"name": "Lattice Local Agent",
|
|
62
|
+
"kind": "on-device-runtime",
|
|
63
|
+
"online": True,
|
|
64
|
+
"platform": platform.platform(),
|
|
65
|
+
"machine": platform.machine(),
|
|
66
|
+
"python": platform.python_version(),
|
|
67
|
+
},
|
|
68
|
+
"handshake": {
|
|
69
|
+
"ok": True,
|
|
70
|
+
"transport": "in-process",
|
|
71
|
+
"detail": "The local Lattice runtime serves this workspace on-device; no external agent is required.",
|
|
72
|
+
},
|
|
73
|
+
"health": {
|
|
74
|
+
"status": "ok",
|
|
75
|
+
"filesystem_access": True,
|
|
76
|
+
"watcher_available": bool(watch.get("available")),
|
|
77
|
+
},
|
|
78
|
+
"folders": {"connected": len(sources), "watching": watched},
|
|
79
|
+
"watch": watch,
|
|
80
|
+
"sources": sources,
|
|
81
|
+
}
|
|
82
|
+
|
|
42
83
|
@router.post("/local/list")
|
|
43
84
|
async def local_list_endpoint(req: LocalAccessRequest, request: Request):
|
|
44
85
|
current_user = permission_gateway.require_local_user(request)
|
package/latticeai/api/models.py
CHANGED
|
@@ -23,6 +23,39 @@ from fastapi.responses import StreamingResponse
|
|
|
23
23
|
from pydantic import BaseModel
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
def _vision_capability(current_model_id: Optional[str], engines: Any) -> Dict[str, Any]:
|
|
27
|
+
"""Whether the active model can accept image input (VLM).
|
|
28
|
+
|
|
29
|
+
Honest, derived signal for the Chat 'Vision Enabled / Disabled' badge: a
|
|
30
|
+
model is vision-capable only when its compat profile reports
|
|
31
|
+
``supports_vision``. The MLX-VLM engine availability is reported too so the
|
|
32
|
+
UI can explain a disabled badge ("load a vision model" vs "install MLX-VLM").
|
|
33
|
+
"""
|
|
34
|
+
from latticeai.core.model_compat import get_model_profile
|
|
35
|
+
|
|
36
|
+
current_vision = False
|
|
37
|
+
if current_model_id:
|
|
38
|
+
try:
|
|
39
|
+
current_vision = bool(get_model_profile(current_model_id).get("supports_vision"))
|
|
40
|
+
except Exception:
|
|
41
|
+
current_vision = False
|
|
42
|
+
engine_available = False
|
|
43
|
+
try:
|
|
44
|
+
for eng in (engines or []):
|
|
45
|
+
if isinstance(eng, dict) and eng.get("id") in {"local_mlx", "mlx"} and eng.get("installed"):
|
|
46
|
+
engine_available = True
|
|
47
|
+
break
|
|
48
|
+
except Exception:
|
|
49
|
+
engine_available = False
|
|
50
|
+
return {
|
|
51
|
+
"current_model": current_model_id,
|
|
52
|
+
"current_supports_vision": current_vision,
|
|
53
|
+
"engine_available": engine_available,
|
|
54
|
+
# The badge is "enabled" only when a vision-capable model is active.
|
|
55
|
+
"enabled": bool(current_vision),
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
26
59
|
class LoadModelRequest(BaseModel):
|
|
27
60
|
model_id: str
|
|
28
61
|
engine: Optional[str] = None
|
|
@@ -257,13 +290,15 @@ def create_models_router(
|
|
|
257
290
|
recommended = _recommended_with_engine_options(
|
|
258
291
|
list(filter_lower_family_versions(ENGINE_MODEL_CATALOG.get("local_mlx", [])))
|
|
259
292
|
)
|
|
293
|
+
engines = await asyncio.to_thread(engine_status)
|
|
260
294
|
return {
|
|
261
295
|
"recommended": recommended,
|
|
262
296
|
"cloud": _router.detected_cloud_models(),
|
|
263
|
-
"engines":
|
|
297
|
+
"engines": engines,
|
|
264
298
|
"loaded": _router.loaded_model_ids,
|
|
265
299
|
"current": _router.current_model_id,
|
|
266
300
|
"compat_profiles": _list_compat_profiles(),
|
|
301
|
+
"vision": _vision_capability(_router.current_model_id, engines),
|
|
267
302
|
}
|
|
268
303
|
|
|
269
304
|
@router.get("/models/compat-profiles")
|
package/latticeai/api/tools.py
CHANGED
|
@@ -197,8 +197,10 @@ def create_tools_router(
|
|
|
197
197
|
recommend_mcps,
|
|
198
198
|
install_mcp,
|
|
199
199
|
mcp_public_item,
|
|
200
|
+
hooks=None,
|
|
200
201
|
) -> APIRouter:
|
|
201
202
|
api_router = APIRouter()
|
|
203
|
+
HOOKS = hooks
|
|
202
204
|
CONFIG = config
|
|
203
205
|
DATA_DIR = data_dir
|
|
204
206
|
STATIC_DIR = static_dir
|
|
@@ -219,10 +221,22 @@ def create_tools_router(
|
|
|
219
221
|
# ── Direct Tool API ───────────────────────────────────────────────────────────
|
|
220
222
|
|
|
221
223
|
def _tool_response(fn, *args):
|
|
224
|
+
tool_name = getattr(fn, "__name__", "tool")
|
|
225
|
+
# ── pre_tool hooks ── a blocking pre_tool hook (e.g. a permission gate)
|
|
226
|
+
# stops the tool call before it runs.
|
|
227
|
+
if HOOKS is not None:
|
|
228
|
+
pre = HOOKS.fire_hook("pre_tool", f"tool.{tool_name}", payload={"tool": tool_name, "argc": len(args)})
|
|
229
|
+
if pre.get("blocked"):
|
|
230
|
+
raise HTTPException(status_code=403, detail=pre.get("block_reason") or f"Tool '{tool_name}' blocked by a pre_tool hook.")
|
|
222
231
|
try:
|
|
223
|
-
|
|
232
|
+
result = fn(*args)
|
|
224
233
|
except ToolError as exc:
|
|
234
|
+
if HOOKS is not None:
|
|
235
|
+
HOOKS.fire_hook("post_tool", f"tool.{tool_name}", payload={"tool": tool_name, "status": "error", "detail": str(exc)})
|
|
225
236
|
raise HTTPException(status_code=400, detail=str(exc))
|
|
237
|
+
if HOOKS is not None:
|
|
238
|
+
HOOKS.fire_hook("post_tool", f"tool.{tool_name}", payload={"tool": tool_name, "status": "ok"})
|
|
239
|
+
return {"status": "ok", "workspace": str(AGENT_ROOT), "result": result}
|
|
226
240
|
|
|
227
241
|
|
|
228
242
|
@api_router.post("/tools/list_dir")
|
|
@@ -431,6 +445,7 @@ def create_tools_router(
|
|
|
431
445
|
classify_sensitive_message=classify_sensitive_message,
|
|
432
446
|
append_audit_event=append_audit_event,
|
|
433
447
|
enforce_rate_limit=enforce_rate_limit,
|
|
448
|
+
hooks=HOOKS,
|
|
434
449
|
)
|
|
435
450
|
|
|
436
451
|
|
|
@@ -57,6 +57,7 @@ def create_workflow_designer_router(
|
|
|
57
57
|
append_audit_event: Callable[..., None],
|
|
58
58
|
ui_file_response: Optional[Callable[[Path], Any]] = None,
|
|
59
59
|
static_dir: Optional[Path] = None,
|
|
60
|
+
hooks: Any = None,
|
|
60
61
|
) -> APIRouter:
|
|
61
62
|
from latticeai.core.workflow_engine import (
|
|
62
63
|
WorkflowEngine,
|
|
@@ -147,7 +148,7 @@ def create_workflow_designer_router(
|
|
|
147
148
|
except FileNotFoundError as exc:
|
|
148
149
|
raise HTTPException(status_code=404, detail=f"Workflow not found: {exc}") from exc
|
|
149
150
|
runners = build_runners(current_user or None, scope)
|
|
150
|
-
engine = WorkflowEngine(runners)
|
|
151
|
+
engine = WorkflowEngine(runners, hooks=hooks)
|
|
151
152
|
result = engine.run(workflow, inputs=req.inputs)
|
|
152
153
|
run = store.record_workflow_run(
|
|
153
154
|
workflow_id=workflow_id,
|