ltcai 8.0.0 → 8.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 +33 -72
- package/docs/CHANGELOG.md +52 -874
- package/docs/DEVELOPMENT.md +26 -3
- package/docs/LEGACY_COMPATIBILITY.md +3 -1
- package/docs/TRUST_MODEL.md +26 -26
- package/docs/WHY_LATTICE.md +28 -25
- package/docs/kg-schema.md +6 -4
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/embeddings.py +9 -1
- package/lattice_brain/graph/schema.py +6 -0
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/computer_use.py +2 -0
- package/latticeai/api/memory.py +13 -0
- package/latticeai/app_factory.py +43 -162
- package/latticeai/brain/__init__.py +1 -1
- package/latticeai/core/config.py +10 -0
- package/latticeai/core/local_embeddings.py +8 -0
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/mcp_registry.py +140 -1
- package/latticeai/core/tool_registry.py +10 -0
- package/latticeai/core/workspace_graph_trace.py +132 -0
- package/latticeai/core/workspace_memory.py +75 -0
- package/latticeai/core/workspace_os.py +135 -1283
- package/latticeai/core/workspace_os_utils.py +132 -0
- package/latticeai/core/workspace_permissions.py +99 -0
- package/latticeai/core/workspace_plugins.py +97 -0
- package/latticeai/core/workspace_runs.py +612 -0
- package/latticeai/core/workspace_skills.py +114 -0
- package/latticeai/core/workspace_snapshots.py +195 -0
- package/latticeai/core/workspace_timeline.py +107 -0
- package/latticeai/runtime/audit_runtime.py +64 -0
- package/latticeai/runtime/config_runtime.py +4 -0
- package/latticeai/runtime/sso_runtime.py +52 -0
- package/latticeai/services/architecture_readiness.py +6 -5
- package/latticeai/services/memory_service.py +215 -7
- package/latticeai/services/model_capability_registry.py +68 -0
- package/latticeai/services/model_engines.py +603 -0
- package/latticeai/services/model_loading.py +482 -0
- package/latticeai/services/model_recommendation.py +5 -0
- package/latticeai/services/model_runtime.py +146 -837
- package/latticeai/services/product_readiness.py +19 -18
- package/package.json +2 -2
- package/scripts/build_frontend_assets.mjs +11 -2
- 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 +11 -102
- package/static/app/assets/Act-D9jIknFd.js +1 -0
- package/static/app/assets/Brain-CFOtWbPN.js +321 -0
- package/static/app/assets/{Capture-B9Tlhzqr.js → Capture-Q4WYzwr5.js} +1 -2
- package/static/app/assets/Library-C5Q2yWee.js +1 -0
- package/static/app/assets/System-BLbjdr1_.js +1 -0
- package/static/app/assets/core-CwxXejkd.js +1 -2
- package/static/app/assets/index-BqammyNu.js +16 -0
- package/static/app/assets/index-ty1iGgZu.css +2 -0
- package/static/app/assets/primitives-Br8uSfZ4.js +1 -0
- package/static/app/assets/{textarea-BZk6ybp5.js → textarea-BnhNs1_X.js} +1 -2
- package/static/app/index.html +3 -10
- package/static/app/theme-boot.js +8 -0
- package/static/sw.js +1 -1
- package/tools/__init__.py +2 -1
- package/tools/computer.py +21 -0
- package/docs/CARRYOVER_AUDIT_v3.6.0.md +0 -61
- package/docs/HANDOVER_v3.6.0.md +0 -46
- package/docs/RUNTIME_HOOK_COVERAGE_v3.5.0.md +0 -56
- package/docs/RUNTIME_HOOK_COVERAGE_v3.6.0.md +0 -49
- package/docs/V2_ARCHITECTURE.md +0 -561
- package/docs/V3_2_AUDIT.md +0 -82
- package/docs/V3_BACKEND_ARCHITECTURE.md +0 -138
- package/docs/V3_FRONTEND.md +0 -140
- package/docs/V4_1_FRONTEND_ARCHITECTURE_REVIEW.md +0 -65
- package/docs/V4_1_FRONTEND_MIGRATION_REPORT.md +0 -70
- package/docs/V4_1_VALIDATION_REPORT.md +0 -47
- package/docs/V4_2_BRAIN_CORE_ARCHITECTURE.md +0 -97
- package/docs/V4_2_STORAGE_MIGRATION_REPORT.md +0 -91
- package/docs/V4_2_VALIDATION_REPORT.md +0 -89
- package/docs/V4_3_2_DEADCODE_AUDIT_REPORT.md +0 -174
- package/docs/V4_3_2_DOCUMENTATION_CLEANUP_REPORT.md +0 -81
- package/docs/V4_3_2_GITHUB_VERCEL_CHECK_REPORT.md +0 -75
- package/docs/V4_3_2_GRAPH_UX_REPORT.md +0 -48
- package/docs/V4_3_2_INDEPENDENT_AUDIT_PACKAGE.md +0 -209
- package/docs/V4_3_2_PRODUCT_POLISH_REPORT.md +0 -57
- package/docs/V4_3_2_SELF_AUDIT_REPORT.md +0 -63
- package/docs/V4_3_2_VALIDATION_REPORT.md +0 -97
- package/docs/V4_3_3_VALIDATION_REPORT.md +0 -46
- package/docs/V4_3_PORTABILITY_ARCHITECTURE.md +0 -69
- package/docs/V4_3_PRIVACY_AUDIT.md +0 -60
- package/docs/V4_3_PRODUCT_HARDENING_REPORT.md +0 -53
- package/docs/V4_3_VALIDATION_REPORT.md +0 -58
- package/docs/V4_4_0_EXTRACTION_REPORT.md +0 -239
- package/docs/V4_5_0_GEMMA_RUNTIME_COMPATIBILITY_REPORT.md +0 -49
- package/docs/V4_5_0_GRAPH_UX_REPORT.md +0 -34
- package/docs/V4_5_0_MODEL_RUNTIME_UX_REPORT.md +0 -40
- package/docs/V4_5_0_ONBOARDING_REPORT.md +0 -31
- package/docs/V4_5_0_PRODUCT_EXPERIENCE_RECOVERY_REPORT.md +0 -49
- package/docs/V4_5_0_VALIDATION_REPORT.md +0 -60
- package/docs/V4_5_1_GRAPH_EXPERIENCE_REPORT.md +0 -33
- package/docs/V4_5_1_MODEL_EXPERIENCE_REPORT.md +0 -37
- package/docs/V4_5_1_NAVIGATION_REPORT.md +0 -37
- package/docs/V4_5_1_ONBOARDING_REPORT.md +0 -29
- package/docs/V4_5_1_PRODUCT_REIMAGINING_REPORT.md +0 -61
- package/docs/V4_5_1_RC_ARTIFACTS.md +0 -44
- package/docs/V4_5_1_UX_REPORT.md +0 -45
- package/docs/V4_5_1_VALIDATION_REPORT.md +0 -55
- package/docs/V4_5_1_VISUAL_DESIGN_REPORT.md +0 -30
- package/docs/V4_6_0_LIVING_BRAIN_EXPERIENCE_REPORT.md +0 -72
- package/docs/V4_6_1_RELEASE_REFRESH_REPORT.md +0 -42
- package/docs/V4_7_0_ADMIN_SEPARATION_REPORT.md +0 -42
- package/docs/V4_7_1_ADMIN_OPERATIONS_REPORT.md +0 -49
- package/docs/V4_7_2_INTUITIVE_BRAIN_UX_REPORT.md +0 -62
- package/docs/V4_BRAIN_ARCHITECTURE.md +0 -322
- package/docs/V4_DIGITAL_BRAIN_RECOVERY.md +0 -555
- package/docs/V4_IMPLEMENTATION_PLAN.md +0 -470
- package/frontend/index.html +0 -24
- package/frontend/openapi.json +0 -15425
- package/frontend/src/App.tsx +0 -243
- package/frontend/src/api/client.ts +0 -580
- package/frontend/src/api/openapi.ts +0 -17892
- package/frontend/src/components/AdminAccessGate.tsx +0 -70
- package/frontend/src/components/FeedbackState.tsx +0 -45
- package/frontend/src/components/LanguageSwitcher.tsx +0 -23
- package/frontend/src/components/LivingBrain.tsx +0 -220
- package/frontend/src/components/ProductFlow.tsx +0 -171
- package/frontend/src/components/WorkspaceProfileSwitcher.tsx +0 -176
- package/frontend/src/components/onboarding/AnalysisScreen.tsx +0 -135
- package/frontend/src/components/onboarding/DownloadConsentPanel.tsx +0 -29
- package/frontend/src/components/onboarding/InstallScreen.tsx +0 -263
- package/frontend/src/components/onboarding/LanguageChooser.tsx +0 -23
- package/frontend/src/components/onboarding/LoginScreen.tsx +0 -131
- package/frontend/src/components/onboarding/ProductFlowScreens.tsx +0 -13
- package/frontend/src/components/onboarding/RecommendationScreen.tsx +0 -116
- package/frontend/src/components/onboarding/recommendationModel.ts +0 -189
- package/frontend/src/components/primitives.tsx +0 -392
- package/frontend/src/components/ui/badge.tsx +0 -27
- package/frontend/src/components/ui/button.tsx +0 -37
- package/frontend/src/components/ui/card.tsx +0 -22
- package/frontend/src/components/ui/input.tsx +0 -16
- package/frontend/src/components/ui/textarea.tsx +0 -16
- package/frontend/src/features/admin/AdminConsole.tsx +0 -334
- package/frontend/src/features/brain/BrainCarePanel.tsx +0 -254
- package/frontend/src/features/brain/BrainComposer.tsx +0 -85
- package/frontend/src/features/brain/BrainConversation.tsx +0 -688
- package/frontend/src/features/brain/BrainGraphLayer.tsx +0 -365
- package/frontend/src/features/brain/BrainHome.tsx +0 -624
- package/frontend/src/features/brain/BrainMemoryLayer.tsx +0 -45
- package/frontend/src/features/brain/BrainOverviewPanel.tsx +0 -149
- package/frontend/src/features/brain/BrainRelationshipLayer.tsx +0 -43
- package/frontend/src/features/brain/DepthEmergence.tsx +0 -53
- package/frontend/src/features/brain/brainData.ts +0 -246
- package/frontend/src/features/brain/graphLayout.ts +0 -37
- package/frontend/src/features/brain/types.ts +0 -150
- package/frontend/src/features/review/ReviewCard.tsx +0 -100
- package/frontend/src/features/review/ReviewInbox.tsx +0 -127
- package/frontend/src/features/review/reviewHelpers.ts +0 -69
- package/frontend/src/i18n.ts +0 -1303
- package/frontend/src/lib/utils.ts +0 -33
- package/frontend/src/main.tsx +0 -23
- package/frontend/src/pages/Act.tsx +0 -458
- package/frontend/src/pages/Ask.tsx +0 -14
- package/frontend/src/pages/Brain.tsx +0 -914
- package/frontend/src/pages/Capture.tsx +0 -258
- package/frontend/src/pages/Library.tsx +0 -486
- package/frontend/src/pages/System.tsx +0 -621
- package/frontend/src/routes.ts +0 -92
- package/frontend/src/store/appStore.ts +0 -94
- package/frontend/src/styles.css +0 -6579
- package/static/app/assets/Act-DOvf59ru.js +0 -2
- package/static/app/assets/Act-DOvf59ru.js.map +0 -1
- package/static/app/assets/Brain-C7_0mEiI.js +0 -322
- package/static/app/assets/Brain-C7_0mEiI.js.map +0 -1
- package/static/app/assets/Capture-B9Tlhzqr.js.map +0 -1
- package/static/app/assets/Library-BJPEEm5O.js +0 -2
- package/static/app/assets/Library-BJPEEm5O.js.map +0 -1
- package/static/app/assets/System-D6t9jo9V.js +0 -2
- package/static/app/assets/System-D6t9jo9V.js.map +0 -1
- package/static/app/assets/core-CwxXejkd.js.map +0 -1
- package/static/app/assets/index-C7g26IF6.css +0 -2
- package/static/app/assets/index-DbcEYJQ2.js +0 -17
- package/static/app/assets/index-DbcEYJQ2.js.map +0 -1
- package/static/app/assets/primitives-CD38lt4n.js +0 -2
- package/static/app/assets/primitives-CD38lt4n.js.map +0 -1
- package/static/app/assets/textarea-BZk6ybp5.js.map +0 -1
package/docs/DEVELOPMENT.md
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
# Lattice AI Development
|
|
2
2
|
|
|
3
|
+
Current release: **8.2.0 - Brain Brief**.
|
|
4
|
+
|
|
3
5
|
This document is for contributors working on the local-first Digital Brain
|
|
4
6
|
codebase. Product positioning and quick start stay in `README.md`; release
|
|
5
|
-
history
|
|
7
|
+
history is intentionally limited to 7.0.0-8.2.0 in `docs/CHANGELOG.md` and
|
|
8
|
+
`RELEASE.md`.
|
|
6
9
|
|
|
7
10
|
## Product Contract
|
|
8
11
|
|
|
@@ -77,9 +80,21 @@ Runtime assembly seams live under `latticeai.runtime`:
|
|
|
77
80
|
- `config_runtime.py` derives app config values from `Config`;
|
|
78
81
|
- `security_runtime.py` applies trusted proxy/security-derived settings;
|
|
79
82
|
- `brain_runtime.py` constructs Brain Core and conversation primitives.
|
|
83
|
+
- model/runtime, ToolRegistry/MCP, router, static asset, and lifespan seams
|
|
84
|
+
should continue moving out of monolithic app-factory helpers.
|
|
85
|
+
|
|
86
|
+
Future extraction should continue with AgentRuntime, ToolRegistry, config,
|
|
87
|
+
server decomposition, and Knowledge Graph stabilization in that order when
|
|
88
|
+
architectural debt is present.
|
|
89
|
+
|
|
90
|
+
## Runtime Hook Coverage
|
|
80
91
|
|
|
81
|
-
|
|
82
|
-
|
|
92
|
+
Knowledge Graph ingestion paths must continue to pass through the shared
|
|
93
|
+
pre-tool/post-tool lifecycle:
|
|
94
|
+
|
|
95
|
+
- browser `read-url` ingestion dispatches `tool.kg_ingest.*` events;
|
|
96
|
+
- browser `ingest-current-tab` ingestion dispatches the same lifecycle;
|
|
97
|
+
- `IngestionPipeline` remains the common ingestion boundary behind those routes.
|
|
83
98
|
|
|
84
99
|
## Documentation Sync
|
|
85
100
|
|
|
@@ -96,3 +111,11 @@ For user-facing, API, runtime, release, or packaging changes, check:
|
|
|
96
111
|
|
|
97
112
|
Release/publish examples must use exact target-version filenames. Do not
|
|
98
113
|
document wildcard artifact upload commands.
|
|
114
|
+
|
|
115
|
+
For 8.2.0 release work, exact artifacts are:
|
|
116
|
+
|
|
117
|
+
- `dist/ltcai-8.2.0-py3-none-any.whl`
|
|
118
|
+
- `dist/ltcai-8.2.0.tar.gz`
|
|
119
|
+
- `ltcai-8.2.0.tgz`
|
|
120
|
+
- `dist/ltcai-8.2.0.vsix`
|
|
121
|
+
- `src-tauri/target/release/bundle/dmg/Lattice AI_8.2.0_aarch64.dmg`
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# Legacy Compatibility Map
|
|
2
2
|
|
|
3
|
+
Current release: **8.2.0 - Brain Brief**.
|
|
4
|
+
|
|
3
5
|
Lattice AI is moving toward a smaller, modular architecture centered on
|
|
4
6
|
`lattice_brain`, `latticeai.services`, `latticeai.api`, and `latticeai.runtime`.
|
|
5
7
|
Some root-level modules remain packaged for compatibility with older imports,
|
|
6
8
|
CLI entrypoints, or extension workflows. Their presence does not define the
|
|
7
|
-
current architecture.
|
|
9
|
+
current 8.2.0 architecture.
|
|
8
10
|
|
|
9
11
|
## Current Policy
|
|
10
12
|
|
package/docs/TRUST_MODEL.md
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# Lattice AI Trust Model
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Current release: **8.2.0 - Brain Brief**.
|
|
4
|
+
|
|
5
|
+
Lattice AI is local-first, explicit about external communication, and honest
|
|
6
|
+
when a capability is unavailable.
|
|
5
7
|
|
|
6
8
|
## Local By Default
|
|
7
9
|
|
|
@@ -9,58 +11,56 @@ By default, Lattice AI binds the API to `127.0.0.1`, stores Brain data under the
|
|
|
9
11
|
local data directory, and does not send prompts, documents, graph content, or
|
|
10
12
|
archives to Lattice-owned servers.
|
|
11
13
|
|
|
12
|
-
Local data
|
|
14
|
+
Local data can include:
|
|
13
15
|
|
|
14
|
-
-
|
|
15
|
-
- conversations and
|
|
16
|
-
- Knowledge Graph nodes, edges, provenance, and
|
|
17
|
-
- uploaded document blobs;
|
|
16
|
+
- profile and session metadata;
|
|
17
|
+
- conversations, memories, decisions, and workflow history;
|
|
18
|
+
- Knowledge Graph nodes, edges, provenance, and indexes;
|
|
19
|
+
- uploaded document blobs and extracted text;
|
|
18
20
|
- audit and admin operation logs;
|
|
19
21
|
- backups and encrypted `.latticebrain` archives.
|
|
20
22
|
|
|
21
23
|
## Explicit External Paths
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
action or configuration:
|
|
25
|
+
External communication requires configuration plus a user/admin action:
|
|
25
26
|
|
|
26
|
-
- model
|
|
27
|
-
-
|
|
27
|
+
- cloud model calls after keys are configured and a cloud model is selected;
|
|
28
|
+
- model downloads from registries after install consent;
|
|
28
29
|
- Telegram bridge after the integration is enabled;
|
|
29
|
-
- Brain Network peer actions after pairing
|
|
30
|
+
- Brain Network peer actions after pairing;
|
|
30
31
|
- Docker/Postgres setup after opt-in scale configuration;
|
|
31
|
-
- update checks only when
|
|
32
|
-
- remote
|
|
32
|
+
- update checks only when enabled;
|
|
33
|
+
- marketplace or remote registry refreshes only when invoked.
|
|
33
34
|
|
|
34
35
|
Token presence alone must not start external communication.
|
|
35
36
|
|
|
36
37
|
## Consent And Honesty Gates
|
|
37
38
|
|
|
38
|
-
Lattice AI should fail closed or
|
|
39
|
+
Lattice AI should fail closed or show an unavailable state for:
|
|
39
40
|
|
|
40
41
|
- no model loaded;
|
|
41
|
-
- local model not installed;
|
|
42
|
-
- installed model not loaded;
|
|
42
|
+
- local model not installed or not loaded;
|
|
43
43
|
- missing cloud key;
|
|
44
44
|
- deterministic/model-free preview;
|
|
45
45
|
- dry-run versus real execution;
|
|
46
46
|
- no graph/context evidence available;
|
|
47
47
|
- unavailable external integration;
|
|
48
48
|
- wrong archive passphrase;
|
|
49
|
-
- archive
|
|
49
|
+
- archive tampering, unsupported archive versions, or path traversal.
|
|
50
50
|
|
|
51
|
-
##
|
|
51
|
+
## Product Boundary
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
Normal Brain use is conversation, memory, topics, relationships, model state,
|
|
54
|
+
source capture, and Knowledge Graph exploration. Admin Console is for users,
|
|
55
55
|
roles, audit logs, security events, retention, and operations. Admin visibility
|
|
56
56
|
does not mean secrets should appear in clear text.
|
|
57
57
|
|
|
58
|
-
## Known
|
|
58
|
+
## Known Limits
|
|
59
59
|
|
|
60
|
-
- Local files are only as protected as the user's machine, account, backups,
|
|
61
|
-
disk encryption.
|
|
62
|
-
- Cloud model prompts follow the selected provider's policy
|
|
60
|
+
- Local files are only as protected as the user's machine, OS account, backups,
|
|
61
|
+
and disk encryption.
|
|
62
|
+
- Cloud model prompts follow the selected provider's policy once the user
|
|
63
|
+
explicitly chooses that provider.
|
|
63
64
|
- A local admin can inspect local files and process memory outside Lattice AI.
|
|
64
65
|
- Marketplace and model registries are third-party services when explicitly
|
|
65
66
|
contacted.
|
|
66
|
-
|
package/docs/WHY_LATTICE.md
CHANGED
|
@@ -1,51 +1,54 @@
|
|
|
1
1
|
# Why Lattice AI Exists
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Current release: **8.2.0 - Brain Brief**.
|
|
4
|
+
|
|
5
|
+
**Lattice AI is a local-first Digital Brain that keeps your knowledge durable
|
|
6
|
+
across any AI model.**
|
|
4
7
|
|
|
5
8
|
**모델은 바꿔도, 내 지식은 남는 로컬 AI 브레인.**
|
|
6
9
|
|
|
7
|
-
AI models change quickly. A model you use today may be replaced
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
asset is not the model. The durable asset is your Brain.
|
|
10
|
+
AI models change quickly. A model you use today may be replaced tomorrow, and a
|
|
11
|
+
cloud chat history can become scattered across tools. Lattice AI exists so the
|
|
12
|
+
durable asset is not the model. The durable asset is your Brain.
|
|
11
13
|
|
|
12
14
|
## The Problem
|
|
13
15
|
|
|
14
|
-
Most AI products
|
|
15
|
-
material. That
|
|
16
|
+
Most AI products start with a model and treat your context as temporary prompt
|
|
17
|
+
material. That breaks down for real work:
|
|
16
18
|
|
|
17
19
|
- project decisions disappear into old chats;
|
|
18
|
-
- documents and notes
|
|
20
|
+
- documents and notes stay disconnected from conversations;
|
|
19
21
|
- switching models often means losing useful context;
|
|
20
|
-
- graph
|
|
21
|
-
- cloud-only products make
|
|
22
|
+
- graph tools expose implementation detail before user value;
|
|
23
|
+
- cloud-only products make your knowledge hard to inspect, back up, or move.
|
|
22
24
|
|
|
23
25
|
## The Lattice Answer
|
|
24
26
|
|
|
25
|
-
Lattice AI
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
Lattice AI keeps conversations, documents, memories, decisions, relationships,
|
|
28
|
+
and source provenance in a private local Brain. Models can be local, cloud,
|
|
29
|
+
current, or future. The Brain remains.
|
|
30
|
+
|
|
31
|
+
The graph matters, but it is not the product identity. The product identity is a
|
|
32
|
+
local-first Digital Brain: a place where the user can talk, add sources, watch
|
|
33
|
+
memory grow, and inspect the Knowledge Graph when they need proof.
|
|
28
34
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
Chat, memory, topics, relationships, ownership, backup, and graph exploration.
|
|
33
|
-
Advanced admin logs, roles, hooks, workflows, Telegram, Brain Network, Docker,
|
|
34
|
-
Postgres, and plugin details stay outside the normal user flow.
|
|
35
|
+
In 8.2.0 the first screen is intentionally not a dashboard. The living Brain,
|
|
36
|
+
conversation composer, and evidence-backed Brain Brief appear together so the
|
|
37
|
+
user immediately knows what matters and what to do next.
|
|
35
38
|
|
|
36
39
|
## Practical Reasons To Use It
|
|
37
40
|
|
|
38
|
-
- Ask what the team decided
|
|
39
|
-
-
|
|
40
|
-
- Prepare
|
|
41
|
-
- Preserve context when moving
|
|
42
|
-
- Export or
|
|
41
|
+
- Ask what the team decided and see the source.
|
|
42
|
+
- Add documents and build a searchable private memory.
|
|
43
|
+
- Prepare from old notes, project decisions, and files.
|
|
44
|
+
- Preserve context when moving between models.
|
|
45
|
+
- Export or restore the Brain as an encrypted `.latticebrain` archive.
|
|
43
46
|
- Use Korean or English without changing the underlying Brain.
|
|
44
47
|
- Avoid fake answers when no model or evidence is available.
|
|
45
48
|
|
|
46
49
|
## What Lattice AI Is Not
|
|
47
50
|
|
|
48
|
-
- Not
|
|
51
|
+
- Not hosted SaaS by default.
|
|
49
52
|
- Not just a model launcher.
|
|
50
53
|
- Not just a graph viewer.
|
|
51
54
|
- Not a generic dashboard.
|
package/docs/kg-schema.md
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
# Knowledge Graph
|
|
1
|
+
# Knowledge Graph Schema
|
|
2
|
+
|
|
3
|
+
Current release: **8.2.0 - Brain Brief**.
|
|
2
4
|
|
|
3
5
|
명세 출처: `lattice_ai_full_spec.pptx` 슬라이드 20·21·22
|
|
4
6
|
구현: `kg_schema.py`
|
|
@@ -80,15 +82,15 @@ Edge {
|
|
|
80
82
|
|
|
81
83
|
**엔드포인트 룰은 권고 사항이다 (스키마 문서 기준).** 코드에는 엔드포인트 페어
|
|
82
84
|
검증기가 존재하지 않는다 — `validate_endpoints` 는 구현된 적이 없으며, 쓰기
|
|
83
|
-
경로는 타입 페어를 거부하지 않는다.
|
|
85
|
+
경로는 타입 페어를 거부하지 않는다. 현재 쓰기 정규화는 *타입 어휘* 를
|
|
84
86
|
강제한다: `_upsert_edge` 가 모든 엣지 타입을 canonical `EdgeType` 값으로
|
|
85
87
|
정규화하므로 자유 문자열 타입은 더 이상 생성되지 않는다.
|
|
86
88
|
|
|
87
89
|
---
|
|
88
90
|
|
|
89
|
-
##
|
|
91
|
+
## Current Knowledge Graph First 엔티티/관계
|
|
90
92
|
|
|
91
|
-
|
|
93
|
+
8.2.0 은 "모든 데이터 소스가 Knowledge Graph 로 수렴한다"는 원칙을 1급 스키마로
|
|
92
94
|
승격한다. 아래 타입은 **추가형(additive)**이다 — 기존 enum/legacy 매핑을 깨지 않고
|
|
93
95
|
`from_legacy` 가 무손실로 정규화하며, 알 수 없는 타입은 여전히 `CONCEPT`/`MENTIONS` 로
|
|
94
96
|
폴백한다. 스키마는 **확장 가능**하게 유지한다: 새 도메인 엔티티는 enum 멤버 1개 +
|
|
@@ -15,6 +15,10 @@ DEFAULT_EMBEDDING_DIM = int(os.getenv("LATTICEAI_VECTOR_DIM", "384"))
|
|
|
15
15
|
EMBEDDING_MODEL_ID = f"lattice-local-hash-v1:{DEFAULT_EMBEDDING_DIM}"
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
def embedding_model_id(dim: int) -> str:
|
|
19
|
+
return f"lattice-local-hash-v1:{int(dim)}"
|
|
20
|
+
|
|
21
|
+
|
|
18
22
|
def _tokenize(text: str) -> List[str]:
|
|
19
23
|
raw = str(text or "").lower()
|
|
20
24
|
tokens = re.findall(r"[a-z0-9][a-z0-9_.:/+-]{1,}|[가-힣]{2,}", raw)
|
|
@@ -49,6 +53,10 @@ class LocalEmbeddingModel:
|
|
|
49
53
|
dim: int = DEFAULT_EMBEDDING_DIM
|
|
50
54
|
model_id: str = EMBEDDING_MODEL_ID
|
|
51
55
|
|
|
56
|
+
def __post_init__(self) -> None:
|
|
57
|
+
if self.model_id == EMBEDDING_MODEL_ID and self.dim != DEFAULT_EMBEDDING_DIM:
|
|
58
|
+
object.__setattr__(self, "model_id", embedding_model_id(self.dim))
|
|
59
|
+
|
|
52
60
|
def embed(self, text: str) -> List[float]:
|
|
53
61
|
vector = [0.0] * self.dim
|
|
54
62
|
features = _tokenize(text)
|
|
@@ -79,4 +87,4 @@ class LocalEmbeddingModel:
|
|
|
79
87
|
return list(struct.unpack(f"<{count}f", payload[: count * 4]))
|
|
80
88
|
|
|
81
89
|
|
|
82
|
-
__all__ = ["DEFAULT_EMBEDDING_DIM", "EMBEDDING_MODEL_ID", "LocalEmbeddingModel"]
|
|
90
|
+
__all__ = ["DEFAULT_EMBEDDING_DIM", "EMBEDDING_MODEL_ID", "LocalEmbeddingModel", "embedding_model_id"]
|
|
@@ -56,6 +56,12 @@ KG_SCHEMA_V2_VERSION = 2
|
|
|
56
56
|
EMBED_DIM = int(os.getenv("LATTICEAI_EMBED_DIM", "1024"))
|
|
57
57
|
|
|
58
58
|
|
|
59
|
+
def set_embed_dim(dim: int) -> None:
|
|
60
|
+
"""Optional way for upper layers to override without direct module import at top level in lattice_brain."""
|
|
61
|
+
global EMBED_DIM
|
|
62
|
+
EMBED_DIM = int(dim) # lattice_brain isolation: no direct latticeai import; callers can pre-set env if using central Config
|
|
63
|
+
|
|
64
|
+
|
|
59
65
|
# ── Node / Edge taxonomy (PPT 슬라이드 20·21) ──────────────────────────────
|
|
60
66
|
class NodeType(str, Enum):
|
|
61
67
|
"""워크스페이스의 모든 ‘명사’.
|
|
@@ -21,7 +21,7 @@ from typing import Any, Callable, Dict, List, Optional
|
|
|
21
21
|
from .contracts import multi_agent_contract
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
MULTI_AGENT_VERSION = "8.
|
|
24
|
+
MULTI_AGENT_VERSION = "8.2.0"
|
|
25
25
|
|
|
26
26
|
AGENT_ROLES = ("researcher", "planner", "executor", "reviewer", "release")
|
|
27
27
|
CORE_PIPELINE = ("planner", "executor", "reviewer")
|
package/latticeai/__init__.py
CHANGED
|
@@ -42,12 +42,14 @@ Available actions:
|
|
|
42
42
|
- computer_scroll: {"action":"computer_scroll","args":{"x":500,"y":300,"direction":"down","clicks":3}}
|
|
43
43
|
- computer_move: {"action":"computer_move","args":{"x":500,"y":300}}
|
|
44
44
|
- computer_drag: {"action":"computer_drag","args":{"x1":100,"y1":100,"x2":500,"y2":500}}
|
|
45
|
+
- vision_analyze: {"action":"vision_analyze","args":{"image_b64": "...", "prompt": "What do you see on screen?"}} — use after screenshot to let VLM describe/answer about the image (only when multimodal model loaded)
|
|
45
46
|
- final: {"action":"final","message":"Korean summary of what was accomplished"}
|
|
46
47
|
|
|
47
48
|
Rules:
|
|
48
49
|
- Respond with exactly ONE JSON object. No markdown, no extra text.
|
|
49
50
|
- Do not take screenshots for simple app launch, URL opening, keyboard shortcuts, or non-visual tasks.
|
|
50
51
|
- Take a screenshot before coordinate-based clicks/drags or when the task explicitly asks you to inspect the screen.
|
|
52
|
+
- After screenshot, prefer vision_analyze (with good prompt) over raw b64 in next step when you need to understand what is on screen (especially with multimodal/VLM loaded).
|
|
51
53
|
- After coordinate-based clicking or typing into an unknown focused field, take a screenshot only if verification is necessary.
|
|
52
54
|
- Use coordinates relative to the screen (0,0 is top-left).
|
|
53
55
|
- If a UI element is not visible, scroll or search for it first.
|
package/latticeai/api/memory.py
CHANGED
|
@@ -58,6 +58,19 @@ def create_memory_router(
|
|
|
58
58
|
scope = gate_read(request)
|
|
59
59
|
return service.brain_quality_summary(user_email=user, workspace_id=scope)
|
|
60
60
|
|
|
61
|
+
@router.get("/api/memory/brain-brief")
|
|
62
|
+
async def brain_brief(request: Request, q: str = "", limit: int = 3):
|
|
63
|
+
user = require_user(request)
|
|
64
|
+
scope = gate_read(request)
|
|
65
|
+
active_model = active_model_getter() if active_model_getter else ""
|
|
66
|
+
return service.brain_brief(
|
|
67
|
+
user_email=user,
|
|
68
|
+
workspace_id=scope,
|
|
69
|
+
active_model=active_model,
|
|
70
|
+
recall_query=q,
|
|
71
|
+
limit=limit,
|
|
72
|
+
)
|
|
73
|
+
|
|
61
74
|
@router.get("/api/memory/brain-proof")
|
|
62
75
|
async def brain_proof(request: Request, q: str = "", limit: int = 3):
|
|
63
76
|
user = require_user(request)
|
package/latticeai/app_factory.py
CHANGED
|
@@ -44,6 +44,8 @@ from latticeai.runtime.platform_services_runtime import (
|
|
|
44
44
|
from latticeai.runtime.platform_runtime_wiring import build_platform_automation_runtime
|
|
45
45
|
from latticeai.runtime.persistence_runtime import build_persistence_runtime
|
|
46
46
|
from latticeai.runtime.review_wiring import build_review_run_now_runner
|
|
47
|
+
from latticeai.runtime.sso_runtime import build_sso_runtime
|
|
48
|
+
from latticeai.runtime.audit_runtime import build_audit_runtime
|
|
47
49
|
from latticeai.runtime.router_registration import (
|
|
48
50
|
build_auth_admin_security_router_bundle,
|
|
49
51
|
build_static_routes_bundle,
|
|
@@ -77,8 +79,6 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
77
79
|
import re
|
|
78
80
|
import secrets
|
|
79
81
|
import threading
|
|
80
|
-
import subprocess
|
|
81
|
-
import sys
|
|
82
82
|
import time
|
|
83
83
|
from pathlib import Path
|
|
84
84
|
|
|
@@ -95,6 +95,7 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
95
95
|
|
|
96
96
|
from latticeai.models.router import LLMRouter, normalize_branding
|
|
97
97
|
from lattice_brain._kg_common import set_llm_router
|
|
98
|
+
from lattice_brain.graph.schema import set_embed_dim
|
|
98
99
|
from latticeai.core.security import (
|
|
99
100
|
hash_password,
|
|
100
101
|
verify_password,
|
|
@@ -107,7 +108,6 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
107
108
|
)
|
|
108
109
|
from latticeai.core.audit import (
|
|
109
110
|
get_audit_log as _get_audit_log,
|
|
110
|
-
append_audit_event as _append_audit_event,
|
|
111
111
|
classify_sensitive_message as _classify_sensitive_message,
|
|
112
112
|
build_sensitivity_report as _build_sensitivity_report,
|
|
113
113
|
build_admin_audit_report as _build_admin_audit_report,
|
|
@@ -195,8 +195,10 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
195
195
|
)
|
|
196
196
|
from latticeai.core.tool_registry import TOOL_CATALOG_BRIEF as _TOOL_CATALOG_BRIEF # noqa: F401
|
|
197
197
|
from latticeai.core.mcp_registry import (
|
|
198
|
-
|
|
199
|
-
|
|
198
|
+
_fetch_skills_marketplace,
|
|
199
|
+
install_skill,
|
|
200
|
+
SKILLS_DIR,
|
|
201
|
+
create_mcp_install_state,
|
|
200
202
|
)
|
|
201
203
|
from latticeai.services.p_reinforce import PReinforceGardener
|
|
202
204
|
from setup_wizard import get_recommendations, scan_environment
|
|
@@ -249,29 +251,12 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
249
251
|
SSO_CLIENT_SECRET = _security_runtime["SSO_CLIENT_SECRET"]
|
|
250
252
|
SSO_REDIRECT_URI = _security_runtime["SSO_REDIRECT_URI"]
|
|
251
253
|
SSO_PROVIDER_NAME = _security_runtime["SSO_PROVIDER_NAME"]
|
|
252
|
-
_sso_discovery_cache: Optional[Dict] = None
|
|
253
|
-
_sso_discovery_cache_url: str = ""
|
|
254
|
-
_sso_states: Dict[str, float] = {} # state → timestamp (CSRF protection)
|
|
255
254
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
return _sso_discovery_cache
|
|
262
|
-
if not discovery_url:
|
|
263
|
-
return None
|
|
264
|
-
try:
|
|
265
|
-
import httpx as _httpx
|
|
266
|
-
async with _httpx.AsyncClient() as c:
|
|
267
|
-
r = await c.get(discovery_url, timeout=10)
|
|
268
|
-
r.raise_for_status()
|
|
269
|
-
_sso_discovery_cache = r.json()
|
|
270
|
-
_sso_discovery_cache_url = discovery_url
|
|
271
|
-
except Exception as e:
|
|
272
|
-
logging.warning("SSO discovery failed: %s", e)
|
|
273
|
-
return None
|
|
274
|
-
return _sso_discovery_cache
|
|
255
|
+
# SSO cache + discovery built after get_sso_settings is defined (see below)
|
|
256
|
+
_sso_discovery_cache = None
|
|
257
|
+
_sso_discovery_cache_url = ""
|
|
258
|
+
_sso_states: Dict[str, float] = {}
|
|
259
|
+
_get_sso_discovery = None # filled after def below
|
|
275
260
|
|
|
276
261
|
# ── Password hashing — used directly from latticeai.core.security ──────────────
|
|
277
262
|
# (hash_password / verify_password are imported above; no local wrapper needed)
|
|
@@ -322,6 +307,15 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
322
307
|
MCP_FILE = DATA_DIR / "mcp_installs.json"
|
|
323
308
|
AUDIT_FILE = DATA_DIR / "audit_log.json"
|
|
324
309
|
SSO_FILE = DATA_DIR / "sso_config.json"
|
|
310
|
+
|
|
311
|
+
# MCP state extracted to mcp_registry.create_mcp_install_state (server decomp)
|
|
312
|
+
_mcp_state = create_mcp_install_state(DATA_DIR)
|
|
313
|
+
load_mcp_installs = _mcp_state["load_mcp_installs"]
|
|
314
|
+
save_mcp_installs = _mcp_state["save_mcp_installs"]
|
|
315
|
+
mcp_public_item = _mcp_state["mcp_public_item"]
|
|
316
|
+
recommend_mcps = _mcp_state["recommend_mcps"]
|
|
317
|
+
install_mcp = _mcp_state["install_mcp"]
|
|
318
|
+
|
|
325
319
|
# Resolve the configured embedding provider once at startup. Degrades to the
|
|
326
320
|
# offline hash fallback when the requested provider is unavailable, while
|
|
327
321
|
# recording the requested-vs-active provider for the Embeddings status surface.
|
|
@@ -346,6 +340,7 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
346
340
|
extra={"target": CONFIG.embedding_custom_target},
|
|
347
341
|
probe=_embedding_provider not in {"", "hash", "local", "fallback"},
|
|
348
342
|
)
|
|
343
|
+
set_embed_dim(int(getattr(EMBEDDER, "dim", None) or _embedding_dim or 384))
|
|
349
344
|
if EMBEDDER.fell_back:
|
|
350
345
|
logging.warning("Embedding provider %s unavailable: %s", EMBEDDER.requested, EMBEDDER.detail)
|
|
351
346
|
STORAGE_ENGINE = storage_from_env(os.environ, data_dir=DATA_DIR) if ENABLE_GRAPH else None
|
|
@@ -470,6 +465,17 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
470
465
|
def get_sso_settings() -> Dict[str, object]:
|
|
471
466
|
return load_sso_config()
|
|
472
467
|
|
|
468
|
+
# Build SSO runtime (cache + helper) now that get_sso_settings exists in scope.
|
|
469
|
+
if _get_sso_discovery is None:
|
|
470
|
+
_sso = build_sso_runtime(
|
|
471
|
+
get_sso_settings=get_sso_settings,
|
|
472
|
+
logging=logging,
|
|
473
|
+
)
|
|
474
|
+
_sso_discovery_cache = _sso["_sso_discovery_cache"]
|
|
475
|
+
_sso_discovery_cache_url = _sso["_sso_discovery_cache_url"]
|
|
476
|
+
_sso_states = _sso["_sso_states"]
|
|
477
|
+
_get_sso_discovery = _sso["_get_sso_discovery"]
|
|
478
|
+
|
|
473
479
|
def public_sso_config(config: Optional[Dict[str, object]] = None) -> Dict[str, object]:
|
|
474
480
|
cfg = config or get_sso_settings()
|
|
475
481
|
return {
|
|
@@ -546,144 +552,10 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
546
552
|
with open(VPC_FILE, "w", encoding="utf-8") as f:
|
|
547
553
|
json.dump(config, f, ensure_ascii=False, indent=2)
|
|
548
554
|
|
|
549
|
-
def load_mcp_installs() -> Dict:
|
|
550
|
-
if not os.path.exists(MCP_FILE):
|
|
551
|
-
return {"installed": {}, "updated_at": None}
|
|
552
|
-
try:
|
|
553
|
-
with open(MCP_FILE, "r", encoding="utf-8") as f:
|
|
554
|
-
data = json.load(f)
|
|
555
|
-
if "installed" not in data:
|
|
556
|
-
data["installed"] = {}
|
|
557
|
-
return data
|
|
558
|
-
except Exception as e:
|
|
559
|
-
logging.warning("load_mcp_installs failed: %s", e)
|
|
560
|
-
return {"installed": {}, "updated_at": None}
|
|
561
|
-
|
|
562
|
-
def save_mcp_installs(data: Dict):
|
|
563
|
-
data["updated_at"] = datetime.now().isoformat()
|
|
564
|
-
with open(MCP_FILE, "w", encoding="utf-8") as f:
|
|
565
|
-
json.dump(data, f, ensure_ascii=False, indent=2)
|
|
566
|
-
|
|
567
|
-
def mcp_public_item(item: Dict, installed_state: Dict) -> Dict:
|
|
568
|
-
state = installed_state.get(item["id"]) or {}
|
|
569
|
-
installed = item["install_mode"] in {"builtin", "bundled"} or bool(state.get("installed"))
|
|
570
|
-
connector_pending = item["install_mode"] == "connector" and not state.get("authenticated")
|
|
571
|
-
authenticated = item["install_mode"] != "connector" or bool(state.get("authenticated"))
|
|
572
|
-
return {
|
|
573
|
-
"id": item["id"],
|
|
574
|
-
"name": item["name"],
|
|
575
|
-
"category": item.get("category", ""),
|
|
576
|
-
"install_mode": item["install_mode"],
|
|
577
|
-
"description": item.get("description", ""),
|
|
578
|
-
"capabilities": item.get("capabilities", []),
|
|
579
|
-
"connector_url": item.get("connector_url"),
|
|
580
|
-
"external_url": item.get("external_url"),
|
|
581
|
-
"package": item.get("package"),
|
|
582
|
-
"homepage": item.get("homepage"),
|
|
583
|
-
"source": item.get("source", "local"),
|
|
584
|
-
"installed": installed,
|
|
585
|
-
"status": state.get("status") or ("active" if installed and not connector_pending else "needs_auth" if connector_pending else "available"),
|
|
586
|
-
"authenticated": authenticated,
|
|
587
|
-
"updated_at": state.get("updated_at"),
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
async def recommend_mcps(query: str, limit: int = 5) -> List[Dict]:
|
|
591
|
-
text = (query or "").lower()
|
|
592
|
-
installed = load_mcp_installs().get("installed", {})
|
|
593
|
-
registry = await _get_combined_registry()
|
|
594
|
-
scored = []
|
|
595
|
-
for item in registry:
|
|
596
|
-
score = 0
|
|
597
|
-
hits = []
|
|
598
|
-
for keyword in item.get("keywords", []):
|
|
599
|
-
if keyword.lower() in text:
|
|
600
|
-
score += 3 if len(keyword) > 2 else 1
|
|
601
|
-
hits.append(keyword)
|
|
602
|
-
# description 키워드 매칭 (remote 항목 보완)
|
|
603
|
-
if not hits and text:
|
|
604
|
-
desc_words = item.get("description", "").lower().split()
|
|
605
|
-
for word in text.split():
|
|
606
|
-
if len(word) > 2 and word in desc_words:
|
|
607
|
-
score += 1
|
|
608
|
-
hits.append(word)
|
|
609
|
-
if item["id"] == "filesystem" and any(word in text for word in ["만들", "구현", "build", "deploy", "코드", "앱"]):
|
|
610
|
-
score += 2
|
|
611
|
-
if score:
|
|
612
|
-
public = mcp_public_item(item, installed)
|
|
613
|
-
public["score"] = score
|
|
614
|
-
public["matched_keywords"] = hits[:6]
|
|
615
|
-
scored.append(public)
|
|
616
|
-
if not scored:
|
|
617
|
-
fallback_ids = ["filesystem", "browser", "documents"]
|
|
618
|
-
scored = [
|
|
619
|
-
{**mcp_public_item(item, installed), "score": 1, "matched_keywords": []}
|
|
620
|
-
for item in registry
|
|
621
|
-
if item["id"] in fallback_ids
|
|
622
|
-
]
|
|
623
|
-
return sorted(scored, key=lambda item: item["score"], reverse=True)[: max(1, min(limit, 24))]
|
|
624
|
-
|
|
625
|
-
async def install_mcp(mcp_id: str) -> Dict:
|
|
626
|
-
registry = await _get_combined_registry()
|
|
627
|
-
item = next((entry for entry in registry if entry["id"] == mcp_id), None)
|
|
628
|
-
if not item:
|
|
629
|
-
raise HTTPException(status_code=404, detail="MCP를 찾을 수 없습니다.")
|
|
630
|
-
data = load_mcp_installs()
|
|
631
|
-
state = data.setdefault("installed", {})
|
|
632
|
-
status = "active"
|
|
633
|
-
message = "MCP가 활성화되었습니다."
|
|
634
|
-
if item["install_mode"] == "connector":
|
|
635
|
-
status = "needs_auth"
|
|
636
|
-
message = "커넥터 인증이 필요합니다. Codex 앱의 connector 설정에서 계정을 연결하면 바로 사용할 수 있습니다."
|
|
637
|
-
elif item["install_mode"] == "pip":
|
|
638
|
-
packages = item.get("pip_packages") or []
|
|
639
|
-
for pkg in packages:
|
|
640
|
-
completed = subprocess.run(
|
|
641
|
-
[sys.executable, "-m", "pip", "install", "--upgrade", pkg],
|
|
642
|
-
capture_output=True, text=True, timeout=900, check=False,
|
|
643
|
-
)
|
|
644
|
-
if completed.returncode != 0:
|
|
645
|
-
raise HTTPException(status_code=500, detail=completed.stderr[-2000:] or f"{pkg} 설치 실패")
|
|
646
|
-
message = f"필수 패키지 설치 완료: {', '.join(packages)}"
|
|
647
|
-
elif item["install_mode"] == "pypi":
|
|
648
|
-
pkg = item.get("package", "")
|
|
649
|
-
version = item.get("package_version")
|
|
650
|
-
pkg_str = f"{pkg}=={version}" if version else pkg
|
|
651
|
-
completed = subprocess.run(
|
|
652
|
-
[sys.executable, "-m", "pip", "install", pkg_str],
|
|
653
|
-
capture_output=True, text=True, timeout=300, check=False,
|
|
654
|
-
)
|
|
655
|
-
if completed.returncode != 0:
|
|
656
|
-
raise HTTPException(status_code=500, detail=completed.stderr[-2000:] or f"{pkg} 설치 실패")
|
|
657
|
-
message = f"pip 패키지 설치 완료: {pkg_str}"
|
|
658
|
-
elif item["install_mode"] == "npm":
|
|
659
|
-
pkg = item.get("package", "")
|
|
660
|
-
version = item.get("package_version")
|
|
661
|
-
pkg_str = f"{pkg}@{version}" if version else pkg
|
|
662
|
-
completed = subprocess.run(
|
|
663
|
-
["npm", "install", "-g", pkg_str],
|
|
664
|
-
capture_output=True, text=True, timeout=300, check=False,
|
|
665
|
-
)
|
|
666
|
-
if completed.returncode != 0:
|
|
667
|
-
raise HTTPException(status_code=500, detail=completed.stderr[-2000:] or f"{pkg} 설치 실패")
|
|
668
|
-
message = f"npm 패키지 설치 완료: {pkg_str}"
|
|
669
|
-
state[mcp_id] = {
|
|
670
|
-
"installed": True,
|
|
671
|
-
"status": status,
|
|
672
|
-
"authenticated": item["install_mode"] != "connector",
|
|
673
|
-
"updated_at": datetime.now().isoformat(),
|
|
674
|
-
}
|
|
675
|
-
save_mcp_installs(data)
|
|
676
|
-
public = mcp_public_item(item, state)
|
|
677
|
-
public["message"] = message
|
|
678
|
-
return public
|
|
679
555
|
|
|
680
556
|
_history_lock = threading.Lock()
|
|
681
557
|
|
|
682
|
-
|
|
683
|
-
return _get_audit_log(AUDIT_FILE)
|
|
684
|
-
|
|
685
|
-
def append_audit_event(event_type: str, **payload) -> None:
|
|
686
|
-
_append_audit_event(AUDIT_FILE, event_type, **payload)
|
|
558
|
+
# audit build moved after redact_secret_text is defined (see below)
|
|
687
559
|
|
|
688
560
|
def save_to_history(
|
|
689
561
|
role: str,
|
|
@@ -755,6 +627,15 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
755
627
|
def redact_secret_text(text: str) -> str:
|
|
756
628
|
return _redact_secret_text(text)
|
|
757
629
|
|
|
630
|
+
# Build audit now that redact is available
|
|
631
|
+
_audit_rt = build_audit_runtime(
|
|
632
|
+
audit_file=AUDIT_FILE,
|
|
633
|
+
logging=logging,
|
|
634
|
+
redact_fn=redact_secret_text,
|
|
635
|
+
)
|
|
636
|
+
get_audit_log = _audit_rt["get_audit_log"]
|
|
637
|
+
append_audit_event = _audit_rt["append_audit_event"]
|
|
638
|
+
|
|
758
639
|
def get_history():
|
|
759
640
|
try:
|
|
760
641
|
return CONVERSATIONS.history()
|