ltcai 10.0.0 → 10.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 +48 -32
- package/docs/CHANGELOG.md +156 -0
- package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
- package/docs/DEVELOPMENT.md +1 -1
- package/docs/HYBRID_CLOUD_KG_STREAMING.md +101 -0
- package/docs/ONBOARDING.md +1 -1
- package/docs/OPERATIONS.md +1 -1
- package/docs/TRUST_MODEL.md +1 -1
- package/docs/WHY_LATTICE.md +1 -1
- package/docs/kg-schema.md +1 -1
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/archive.py +5 -4
- package/lattice_brain/conversations.py +14 -3
- package/lattice_brain/embeddings.py +12 -2
- package/lattice_brain/graph/_kg_common.py +5 -5
- package/lattice_brain/graph/_kg_fsutil.py +4 -3
- package/lattice_brain/graph/discovery.py +4 -3
- package/lattice_brain/graph/discovery_index.py +0 -1
- package/lattice_brain/graph/documents.py +3 -2
- package/lattice_brain/graph/fusion.py +4 -0
- package/lattice_brain/graph/ingest.py +12 -2
- package/lattice_brain/graph/projection.py +3 -2
- package/lattice_brain/graph/provenance.py +5 -3
- package/lattice_brain/graph/rerank.py +4 -1
- package/lattice_brain/graph/retrieval.py +0 -1
- package/lattice_brain/graph/retrieval_docgen.py +0 -1
- package/lattice_brain/graph/retrieval_reads.py +0 -1
- package/lattice_brain/graph/retrieval_vector.py +0 -1
- package/lattice_brain/graph/schema.py +4 -3
- package/lattice_brain/graph/store.py +18 -4
- package/lattice_brain/graph/write_master.py +46 -1
- package/lattice_brain/ingestion.py +4 -1
- package/lattice_brain/portability.py +5 -2
- package/lattice_brain/quality.py +12 -5
- package/lattice_brain/quiet.py +43 -0
- package/lattice_brain/runtime/agent_runtime.py +12 -8
- package/lattice_brain/runtime/hooks.py +2 -1
- package/lattice_brain/runtime/multi_agent.py +2 -3
- package/lattice_brain/sensitivity.py +94 -0
- package/lattice_brain/storage/base.py +30 -2
- package/lattice_brain/storage/migration.py +3 -2
- package/lattice_brain/storage/postgres.py +2 -2
- package/lattice_brain/storage/sqlite.py +6 -4
- package/lattice_brain/workflow.py +4 -2
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/admin.py +1 -1
- package/latticeai/api/agents.py +4 -2
- package/latticeai/api/auth.py +5 -1
- package/latticeai/api/automation_intelligence.py +2 -1
- package/latticeai/api/browser.py +3 -2
- package/latticeai/api/chat.py +28 -17
- package/latticeai/api/chat_agent_http.py +22 -8
- package/latticeai/api/chat_contracts.py +4 -0
- package/latticeai/api/chat_documents.py +6 -2
- package/latticeai/api/chat_hybrid.py +82 -0
- package/latticeai/api/computer_use.py +8 -3
- package/latticeai/api/knowledge_graph.py +1 -1
- package/latticeai/api/mcp.py +4 -4
- package/latticeai/api/models.py +5 -2
- package/latticeai/api/network_boundary.py +220 -0
- package/latticeai/api/permissions.py +0 -1
- package/latticeai/api/realtime.py +1 -1
- package/latticeai/api/security_dashboard.py +1 -1
- package/latticeai/api/setup.py +16 -3
- package/latticeai/api/static_routes.py +2 -1
- package/latticeai/api/tools.py +12 -8
- package/latticeai/api/voice_capture.py +3 -1
- package/latticeai/api/workflow_designer.py +1 -1
- package/latticeai/api/workspace.py +1 -2
- package/latticeai/app_factory.py +131 -78
- package/latticeai/cli/entrypoint.py +6 -4
- package/latticeai/core/agent.py +55 -495
- package/latticeai/core/agent_eval.py +2 -2
- package/latticeai/core/agent_helpers.py +493 -0
- package/latticeai/core/agent_prompts.py +0 -1
- package/latticeai/core/agent_registry.py +3 -1
- package/latticeai/core/agent_state.py +41 -0
- package/latticeai/core/audit.py +1 -1
- package/latticeai/core/builtin_hooks.py +2 -1
- package/latticeai/core/config.py +0 -1
- package/latticeai/core/embedding_providers.py +12 -1
- package/latticeai/core/file_generation.py +3 -0
- package/latticeai/core/invitations.py +4 -1
- package/latticeai/core/io_utils.py +3 -1
- package/latticeai/core/legacy_compatibility.py +1 -2
- package/latticeai/core/local_embeddings.py +12 -1
- package/latticeai/core/marketplace.py +1 -2
- package/latticeai/core/mcp_registry.py +0 -1
- package/latticeai/core/model_compat.py +1 -1
- package/latticeai/core/model_resolution.py +1 -1
- package/latticeai/core/network_boundary.py +168 -0
- package/latticeai/core/oidc.py +3 -0
- package/latticeai/core/permission_mode.py +6 -6
- package/latticeai/core/plugins.py +3 -2
- package/latticeai/core/policy.py +0 -1
- package/latticeai/core/quiet.py +84 -0
- package/latticeai/core/realtime.py +3 -2
- package/latticeai/core/run_store.py +4 -2
- package/latticeai/core/security.py +4 -0
- package/latticeai/core/users.py +5 -3
- package/latticeai/core/workspace_graph_trace.py +3 -0
- package/latticeai/core/workspace_os.py +65 -273
- package/latticeai/core/workspace_os_constants.py +126 -0
- package/latticeai/core/workspace_os_state.py +180 -0
- package/latticeai/core/workspace_os_utils.py +3 -1
- package/latticeai/core/workspace_permissions.py +1 -0
- package/latticeai/core/workspace_snapshots.py +3 -1
- package/latticeai/core/workspace_timeline.py +3 -1
- package/latticeai/integrations/telegram_bot.py +25 -16
- package/latticeai/models/router.py +6 -3
- package/latticeai/runtime/access_runtime.py +3 -1
- package/latticeai/runtime/audit_runtime.py +3 -2
- package/latticeai/runtime/chat_wiring.py +4 -1
- package/latticeai/runtime/history_runtime.py +1 -1
- package/latticeai/runtime/lifespan_runtime.py +3 -1
- package/latticeai/runtime/network_boundary_wiring.py +124 -0
- package/latticeai/runtime/persistence_runtime.py +3 -1
- package/latticeai/runtime/router_registration.py +11 -1
- package/latticeai/services/architecture_readiness.py +1 -2
- package/latticeai/services/change_proposals.py +2 -1
- package/latticeai/services/cloud_egress_audit.py +85 -0
- package/latticeai/services/cloud_extraction.py +129 -0
- package/latticeai/services/cloud_streaming.py +266 -0
- package/latticeai/services/cloud_token_guard.py +84 -0
- package/latticeai/services/command_center.py +2 -1
- package/latticeai/services/folder_watch.py +3 -0
- package/latticeai/services/funnel_metrics.py +3 -1
- package/latticeai/services/hybrid_chat.py +265 -0
- package/latticeai/services/hybrid_context.py +228 -0
- package/latticeai/services/hybrid_policy.py +178 -0
- package/latticeai/services/memory_service.py +1 -1
- package/latticeai/services/model_catalog.py +10 -1
- package/latticeai/services/model_engines.py +35 -14
- package/latticeai/services/model_loading.py +3 -2
- package/latticeai/services/model_runtime.py +68 -17
- package/latticeai/services/multimodal_streaming.py +123 -0
- package/latticeai/services/network_boundary_service.py +154 -0
- package/latticeai/services/openai_compatible_adapter.py +100 -0
- package/latticeai/services/p_reinforce.py +4 -0
- package/latticeai/services/platform_runtime.py +9 -4
- package/latticeai/services/process_audit.py +0 -1
- package/latticeai/services/product_readiness.py +1 -1
- package/latticeai/services/run_executor.py +3 -2
- package/latticeai/services/search_service.py +1 -4
- package/latticeai/services/setup_detection.py +2 -1
- package/latticeai/services/tool_dispatch.py +7 -2
- package/latticeai/services/upload_service.py +2 -1
- package/latticeai/setup/auto_setup.py +10 -7
- package/latticeai/setup/wizard.py +15 -11
- package/latticeai/tools/__init__.py +3 -3
- package/latticeai/tools/commands.py +7 -7
- package/latticeai/tools/computer.py +3 -2
- package/latticeai/tools/documents.py +10 -9
- package/latticeai/tools/filesystem.py +7 -4
- package/latticeai/tools/knowledge.py +2 -0
- package/latticeai/tools/local_files.py +1 -1
- package/latticeai/tools/network.py +2 -1
- package/package.json +5 -3
- package/scripts/bench_agent_smoke.py +0 -1
- package/scripts/bench_models.py +0 -1
- package/scripts/brain_quality_eval.py +7 -2
- package/scripts/bump_version.py +2 -1
- package/scripts/check_current_release_docs.mjs +1 -1
- package/scripts/migrate_brain_storage.py +5 -1
- package/scripts/profile_kg.py +2 -7
- package/scripts/verify_hf_model_registry.py +2 -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 +37 -37
- package/static/app/assets/{Act-BtCREeN1.js → Act-CbdGD-2i.js} +1 -1
- package/static/app/assets/{AdminConsole-TPeeN18T.js → AdminConsole-LgCkXpnf.js} +1 -1
- package/static/app/assets/{Brain-BKs6JAp0.js → Brain-CoPGJI1L.js} +1 -1
- package/static/app/assets/{BrainHome-BPGOvSd6.js → BrainHome-gVnaxSwp.js} +1 -1
- package/static/app/assets/{BrainSignals-CtzQZ15J.js → BrainSignals-ChxAYHtj.js} +1 -1
- package/static/app/assets/{Capture-1_NaHWqB.js → Capture-DBtgkHZg.js} +1 -1
- package/static/app/assets/{CommandPalette-pqvQOXe4.js → CommandPalette-Ovtv5I0Y.js} +1 -1
- package/static/app/assets/{Library-DhvoPvC7.js → Library-CBia2Fvm.js} +1 -1
- package/static/app/assets/{LivingBrain-DlQ20Q75.js → LivingBrain-CNz-6NSd.js} +1 -1
- package/static/app/assets/{ProductFlow-BZvGDRi_.js → ProductFlow-ePX-Y73J.js} +1 -1
- package/static/app/assets/{ReviewCard-BWgI0D2s.js → ReviewCard-C4mpvkwH.js} +1 -1
- package/static/app/assets/System-BvWNK1zc.js +1 -0
- package/static/app/assets/{activity-Dlfk8YC7.js → activity-CiauPV_3.js} +1 -1
- package/static/app/assets/{bot-CDvUB76P.js → bot-Dwct-Q1x.js} +1 -1
- package/static/app/assets/{brain-xczrohrt.js → brain-BKDW1F0T.js} +1 -1
- package/static/app/assets/{button-SOdH3Oyf.js → button-CCB9Kuw7.js} +1 -1
- package/static/app/assets/{circle-pause-CG1ythH4.js → circle-pause-DNa8WHC5.js} +1 -1
- package/static/app/assets/{circle-play-HXwvjS6W.js → circle-play-8jmxn5mf.js} +1 -1
- package/static/app/assets/{cpu-B0d-rGyk.js → cpu-mxvF3V1I.js} +1 -1
- package/static/app/assets/{download-BGIkTQL6.js → download--SmCcx0p.js} +1 -1
- package/static/app/assets/{folder-open-Dst_Z0_K.js → folder-open-D4Wy5roo.js} +1 -1
- package/static/app/assets/{hard-drive-D53MsWkV.js → hard-drive-BwQcSPlS.js} +1 -1
- package/static/app/assets/index-CQWdDU3z.css +2 -0
- package/static/app/assets/{index-C_IrlQMV.js → index-DDV2YZwM.js} +3 -3
- package/static/app/assets/{input-C5m0riF6.js → input-DFhSjmLS.js} +1 -1
- package/static/app/assets/{network-C5a-E5iS.js → network-Bts7VO94.js} +1 -1
- package/static/app/assets/{primitives-vNXYf58F.js → primitives-BuSMEJY8.js} +1 -1
- package/static/app/assets/search-DZzxhWaQ.js +1 -0
- package/static/app/assets/{shield-alert-Cc-WVXqN.js → shield-alert-BfTO6X_G.js} +1 -1
- package/static/app/assets/{textarea-BkZ0EqVO.js → textarea-BjctW1oh.js} +1 -1
- package/static/app/assets/{useFocusTrap-CTtKbAOU.js → useFocusTrap-CE43-LrA.js} +1 -1
- package/static/app/assets/{useQuery-Dx1fi4Wu.js → useQuery-Bv3ejLL5.js} +1 -1
- package/static/app/assets/{users-BFpQXtEF.js → users-CsNqLZAj.js} +1 -1
- package/static/app/assets/{utils-BA_lmW3J.js → utils-KFFdVG_t.js} +2 -2
- package/static/app/assets/workspace-wdCvdyPF.js +1 -0
- package/static/app/index.html +4 -4
- package/static/sw.js +1 -1
- package/static/app/assets/System-CSMdYLMy.js +0 -1
- package/static/app/assets/index-FxDusbr0.css +0 -2
- package/static/app/assets/search-DhbSgW6m.js +0 -1
- package/static/app/assets/workspace-DBPB0jkX.js +0 -1
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"""Network boundary API — local_only / cloud_allowed dial + Phase 3 policy/UI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Callable, Dict, Optional
|
|
6
|
+
|
|
7
|
+
from fastapi import APIRouter, HTTPException, Request
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
from latticeai.core.network_boundary import network_mode_catalog, normalize_network_mode
|
|
11
|
+
from latticeai.services.cloud_egress_audit import record_cloud_egress
|
|
12
|
+
from latticeai.services.cloud_token_guard import budget_for
|
|
13
|
+
from latticeai.services.hybrid_context import build_minimal_context
|
|
14
|
+
from latticeai.services.hybrid_policy import HybridPolicyService
|
|
15
|
+
from latticeai.services.network_boundary_service import NetworkBoundaryService
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SetNetworkBoundaryRequest(BaseModel):
|
|
19
|
+
mode: str = Field(..., description="local_only | cloud_allowed")
|
|
20
|
+
workspace_id: Optional[str] = None
|
|
21
|
+
acknowledge_risk: bool = False
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class PreviewRequest(BaseModel):
|
|
25
|
+
message: str
|
|
26
|
+
workspace_id: Optional[str] = None
|
|
27
|
+
top_k: int = 6
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class SetNodeSensitivityRequest(BaseModel):
|
|
31
|
+
node_id: str
|
|
32
|
+
local_only: bool = True
|
|
33
|
+
reason: Optional[str] = None
|
|
34
|
+
workspace_id: Optional[str] = None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class SetHybridPolicyRequest(BaseModel):
|
|
38
|
+
workspace_id: Optional[str] = None
|
|
39
|
+
blocked_node_types: Optional[list[str]] = None
|
|
40
|
+
blocked_metadata_flags: Optional[list[str]] = None
|
|
41
|
+
auto_commit: Optional[bool] = None
|
|
42
|
+
allow_multimodal: Optional[bool] = None
|
|
43
|
+
min_extraction_confidence: Optional[float] = None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def create_network_boundary_router(
|
|
47
|
+
*,
|
|
48
|
+
service: NetworkBoundaryService,
|
|
49
|
+
require_user: Callable[..., str],
|
|
50
|
+
knowledge_graph: Any = None,
|
|
51
|
+
policy_service: Optional[HybridPolicyService] = None,
|
|
52
|
+
) -> APIRouter:
|
|
53
|
+
router = APIRouter(tags=["network-boundary"])
|
|
54
|
+
|
|
55
|
+
def _scope(request: Request, workspace_id: Optional[str] = None) -> tuple[str, Optional[str]]:
|
|
56
|
+
user = require_user(request)
|
|
57
|
+
header_ws = request.headers.get("X-Workspace-Id")
|
|
58
|
+
scope = workspace_id or (header_ws.strip() if header_ws else None)
|
|
59
|
+
return user, scope
|
|
60
|
+
|
|
61
|
+
@router.get("/api/network-boundary")
|
|
62
|
+
async def get_network_boundary(
|
|
63
|
+
request: Request,
|
|
64
|
+
workspace_id: Optional[str] = None,
|
|
65
|
+
):
|
|
66
|
+
user, scope = _scope(request, workspace_id)
|
|
67
|
+
payload = service.get(user_email=user, workspace_id=scope)
|
|
68
|
+
scope_key = f"{user or 'anon'}|{scope or 'global'}"
|
|
69
|
+
payload["token_budget"] = budget_for(scope_key).snapshot()
|
|
70
|
+
if policy_service is not None:
|
|
71
|
+
payload["policy"] = policy_service.resolve(
|
|
72
|
+
user_email=user, workspace_id=scope
|
|
73
|
+
)
|
|
74
|
+
return payload
|
|
75
|
+
|
|
76
|
+
@router.get("/api/network-boundary/catalog")
|
|
77
|
+
async def network_boundary_catalog(request: Request):
|
|
78
|
+
require_user(request)
|
|
79
|
+
return {"modes": network_mode_catalog()}
|
|
80
|
+
|
|
81
|
+
@router.get("/api/network-boundary/ui-state")
|
|
82
|
+
async def network_boundary_ui_state(
|
|
83
|
+
request: Request,
|
|
84
|
+
workspace_id: Optional[str] = None,
|
|
85
|
+
):
|
|
86
|
+
"""Compact payload for the progressive-enhancement toggle panel."""
|
|
87
|
+
user, scope = _scope(request, workspace_id)
|
|
88
|
+
mode_payload = service.get(user_email=user, workspace_id=scope)
|
|
89
|
+
policy = (
|
|
90
|
+
policy_service.resolve(user_email=user, workspace_id=scope)
|
|
91
|
+
if policy_service is not None
|
|
92
|
+
else {}
|
|
93
|
+
)
|
|
94
|
+
scope_key = f"{user or 'anon'}|{scope or 'global'}"
|
|
95
|
+
return {
|
|
96
|
+
"mode": mode_payload.get("mode"),
|
|
97
|
+
"label": mode_payload.get("label"),
|
|
98
|
+
"label_ko": mode_payload.get("label_ko"),
|
|
99
|
+
"allows_cloud": mode_payload.get("allows_cloud"),
|
|
100
|
+
"requires_ack": mode_payload.get("requires_ack"),
|
|
101
|
+
"warning_ko": next(
|
|
102
|
+
(
|
|
103
|
+
m.get("warning_ko")
|
|
104
|
+
for m in (mode_payload.get("catalog") or [])
|
|
105
|
+
if m.get("id") == mode_payload.get("mode")
|
|
106
|
+
),
|
|
107
|
+
None,
|
|
108
|
+
),
|
|
109
|
+
"policy": policy,
|
|
110
|
+
"token_budget": budget_for(scope_key).snapshot(),
|
|
111
|
+
"catalog": network_mode_catalog(),
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@router.post("/api/network-boundary")
|
|
115
|
+
async def set_network_boundary(body: SetNetworkBoundaryRequest, request: Request):
|
|
116
|
+
user, scope = _scope(request, body.workspace_id)
|
|
117
|
+
try:
|
|
118
|
+
return service.set_mode(
|
|
119
|
+
normalize_network_mode(body.mode),
|
|
120
|
+
user_email=user,
|
|
121
|
+
workspace_id=scope,
|
|
122
|
+
acknowledge_risk=body.acknowledge_risk,
|
|
123
|
+
source="api",
|
|
124
|
+
)
|
|
125
|
+
except PermissionError as exc:
|
|
126
|
+
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
127
|
+
|
|
128
|
+
@router.post("/api/network-boundary/preview")
|
|
129
|
+
async def preview_cloud_context(body: PreviewRequest, request: Request):
|
|
130
|
+
user, scope = _scope(request, body.workspace_id)
|
|
131
|
+
mode = service.resolve(user_email=user, workspace_id=scope)
|
|
132
|
+
minimal = build_minimal_context(
|
|
133
|
+
body.message,
|
|
134
|
+
store=knowledge_graph,
|
|
135
|
+
mode=mode,
|
|
136
|
+
top_k=max(1, min(int(body.top_k or 6), 12)),
|
|
137
|
+
allowed_workspaces={scope} if scope else None,
|
|
138
|
+
)
|
|
139
|
+
scope_key = f"{user or 'anon'}|{scope or 'global'}"
|
|
140
|
+
budget = budget_for(scope_key)
|
|
141
|
+
refusal = budget.check_turn(minimal.token_estimate)
|
|
142
|
+
return {
|
|
143
|
+
"mode": mode.value,
|
|
144
|
+
"allows_cloud": mode.value == "cloud_allowed",
|
|
145
|
+
"node_ids": minimal.node_ids,
|
|
146
|
+
"keywords": minimal.keywords,
|
|
147
|
+
"titles": [str(n.get("title") or n.get("id") or "") for n in minimal.nodes],
|
|
148
|
+
"types": [str(n.get("type") or "") for n in minimal.nodes],
|
|
149
|
+
"token_estimate": minimal.token_estimate,
|
|
150
|
+
"quality": minimal.quality,
|
|
151
|
+
"compact_preview": minimal.compact_text[:1200],
|
|
152
|
+
"token_budget": budget.snapshot(),
|
|
153
|
+
"would_block": refusal,
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
@router.post("/api/network-boundary/node-sensitivity")
|
|
157
|
+
async def set_node_sensitivity(body: SetNodeSensitivityRequest, request: Request):
|
|
158
|
+
"""Mark one memory as never-leaving (or clear the mark).
|
|
159
|
+
|
|
160
|
+
The cloud filter has always looked for this flag. Until 10.2.0 nothing
|
|
161
|
+
in the product could set it, so the guard could not fire. Ingestion
|
|
162
|
+
stamps secret-bearing paths automatically; this covers what a path
|
|
163
|
+
cannot tell you — a note whose *content* is private.
|
|
164
|
+
"""
|
|
165
|
+
user, scope = _scope(request, body.workspace_id)
|
|
166
|
+
if knowledge_graph is None or not hasattr(knowledge_graph, "set_node_sensitivity"):
|
|
167
|
+
raise HTTPException(status_code=501, detail="knowledge graph not available")
|
|
168
|
+
result = knowledge_graph.set_node_sensitivity(
|
|
169
|
+
body.node_id, local_only=bool(body.local_only), reason=body.reason
|
|
170
|
+
)
|
|
171
|
+
if not result.get("ok"):
|
|
172
|
+
raise HTTPException(status_code=404, detail=result.get("reason") or "node not found")
|
|
173
|
+
record_cloud_egress(
|
|
174
|
+
node_ids=[body.node_id], token_estimate=0, mode="(policy)",
|
|
175
|
+
provider="(local)", user_email=user, workspace_id=scope,
|
|
176
|
+
outcome="marked_local_only" if body.local_only else "cleared_local_only",
|
|
177
|
+
detail=body.reason,
|
|
178
|
+
)
|
|
179
|
+
return result
|
|
180
|
+
|
|
181
|
+
@router.get("/api/network-boundary/policy")
|
|
182
|
+
async def get_hybrid_policy(
|
|
183
|
+
request: Request,
|
|
184
|
+
workspace_id: Optional[str] = None,
|
|
185
|
+
):
|
|
186
|
+
if policy_service is None:
|
|
187
|
+
raise HTTPException(status_code=501, detail="hybrid policy service not configured")
|
|
188
|
+
user, scope = _scope(request, workspace_id)
|
|
189
|
+
return policy_service.resolve(user_email=user, workspace_id=scope)
|
|
190
|
+
|
|
191
|
+
@router.post("/api/network-boundary/policy")
|
|
192
|
+
async def set_hybrid_policy(body: SetHybridPolicyRequest, request: Request):
|
|
193
|
+
if policy_service is None:
|
|
194
|
+
raise HTTPException(status_code=501, detail="hybrid policy service not configured")
|
|
195
|
+
user, scope = _scope(request, body.workspace_id)
|
|
196
|
+
patch: Dict[str, Any] = {}
|
|
197
|
+
for key in (
|
|
198
|
+
"blocked_node_types",
|
|
199
|
+
"blocked_metadata_flags",
|
|
200
|
+
"auto_commit",
|
|
201
|
+
"allow_multimodal",
|
|
202
|
+
"min_extraction_confidence",
|
|
203
|
+
):
|
|
204
|
+
val = getattr(body, key, None)
|
|
205
|
+
if val is not None:
|
|
206
|
+
patch[key] = val
|
|
207
|
+
return policy_service.set_policy(
|
|
208
|
+
patch, user_email=user, workspace_id=scope, source="api"
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
return router
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
__all__ = [
|
|
215
|
+
"create_network_boundary_router",
|
|
216
|
+
"SetNetworkBoundaryRequest",
|
|
217
|
+
"PreviewRequest",
|
|
218
|
+
"SetHybridPolicyRequest",
|
|
219
|
+
"SetNodeSensitivityRequest",
|
|
220
|
+
]
|
|
@@ -16,8 +16,8 @@ from fastapi import APIRouter, HTTPException, Request
|
|
|
16
16
|
from fastapi.responses import StreamingResponse
|
|
17
17
|
from pydantic import BaseModel
|
|
18
18
|
|
|
19
|
-
from latticeai.api.ui_redirects import app_redirect
|
|
20
19
|
from lattice_brain.runtime.contracts import contract_views
|
|
20
|
+
from latticeai.api.ui_redirects import app_redirect
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class PresenceRequest(BaseModel):
|
|
@@ -195,7 +195,7 @@ def _pdf_report(title: str, rows: List[Dict[str, Any]], overview: Dict[str, Any]
|
|
|
195
195
|
try:
|
|
196
196
|
from reportlab.lib.pagesizes import A4
|
|
197
197
|
from reportlab.lib.styles import getSampleStyleSheet
|
|
198
|
-
from reportlab.platypus import
|
|
198
|
+
from reportlab.platypus import Paragraph, SimpleDocTemplate, Spacer, Table
|
|
199
199
|
except Exception: # pragma: no cover
|
|
200
200
|
return ("PDF library not available\n" + json.dumps(overview, ensure_ascii=False, indent=2)).encode("utf-8")
|
|
201
201
|
|
package/latticeai/api/setup.py
CHANGED
|
@@ -8,15 +8,24 @@ from fastapi import APIRouter, HTTPException, Request
|
|
|
8
8
|
from fastapi.responses import StreamingResponse
|
|
9
9
|
from pydantic import BaseModel
|
|
10
10
|
|
|
11
|
+
from lattice_brain.ingestion import IngestionItem
|
|
12
|
+
from latticeai.models.router import parse_model_ref
|
|
11
13
|
from latticeai.services.process_audit import command_plan
|
|
12
14
|
from latticeai.setup.auto_setup import (
|
|
13
15
|
plan as auto_setup_plan,
|
|
16
|
+
)
|
|
17
|
+
from latticeai.setup.auto_setup import (
|
|
14
18
|
preset as auto_setup_preset,
|
|
19
|
+
)
|
|
20
|
+
from latticeai.setup.auto_setup import (
|
|
15
21
|
probe as auto_setup_probe,
|
|
22
|
+
)
|
|
23
|
+
from latticeai.setup.auto_setup import (
|
|
16
24
|
recommend as auto_setup_recommend,
|
|
25
|
+
)
|
|
26
|
+
from latticeai.setup.auto_setup import (
|
|
17
27
|
verify as auto_setup_verify,
|
|
18
28
|
)
|
|
19
|
-
from latticeai.models.router import parse_model_ref
|
|
20
29
|
from latticeai.setup.demo_corpus import (
|
|
21
30
|
DEMO_DOCUMENTS,
|
|
22
31
|
DEMO_METADATA_FLAG,
|
|
@@ -24,8 +33,12 @@ from latticeai.setup.demo_corpus import (
|
|
|
24
33
|
demo_source_uri,
|
|
25
34
|
suggested_questions,
|
|
26
35
|
)
|
|
27
|
-
from latticeai.setup.wizard import
|
|
28
|
-
|
|
36
|
+
from latticeai.setup.wizard import (
|
|
37
|
+
get_recommendations,
|
|
38
|
+
install_stream,
|
|
39
|
+
open_url,
|
|
40
|
+
scan_environment,
|
|
41
|
+
)
|
|
29
42
|
|
|
30
43
|
|
|
31
44
|
class SetupInstallRequest(BaseModel):
|
|
@@ -15,6 +15,7 @@ from fastapi import APIRouter, Cookie, HTTPException, Request
|
|
|
15
15
|
from fastapi.responses import FileResponse, HTMLResponse
|
|
16
16
|
|
|
17
17
|
from latticeai.api.ui_redirects import app_redirect
|
|
18
|
+
from latticeai.core.quiet import quiet
|
|
18
19
|
|
|
19
20
|
PRODUCTION_CSP = (
|
|
20
21
|
"default-src 'self'; "
|
|
@@ -282,7 +283,7 @@ def create_static_routes_router(
|
|
|
282
283
|
result["gpu_mem_gb"] = round(gpu_bytes / (1024 ** 3), 2)
|
|
283
284
|
result["gpu_mem_pct"] = round(gpu_bytes / total_bytes * 100, 1) if total_bytes else 0.0
|
|
284
285
|
except Exception:
|
|
285
|
-
|
|
286
|
+
quiet()
|
|
286
287
|
except Exception as e:
|
|
287
288
|
result["error"] = str(e)
|
|
288
289
|
return result
|
package/latticeai/api/tools.py
CHANGED
|
@@ -14,23 +14,27 @@ from fastapi import APIRouter, File, HTTPException, Request, UploadFile
|
|
|
14
14
|
from fastapi.responses import FileResponse, Response
|
|
15
15
|
from pydantic import BaseModel
|
|
16
16
|
|
|
17
|
+
from lattice_brain.runtime.hooks import dispatch_tool
|
|
17
18
|
from latticeai.api.computer_use import create_computer_use_router
|
|
18
19
|
from latticeai.api.local_files import create_local_files_router
|
|
19
|
-
from lattice_brain.runtime.hooks import dispatch_tool
|
|
20
20
|
from latticeai.api.mcp import create_mcp_router
|
|
21
21
|
from latticeai.api.permissions import create_permissions_router
|
|
22
|
-
from latticeai.services.
|
|
22
|
+
from latticeai.services.router_context import ToolRouterContext
|
|
23
23
|
from latticeai.services.tool_dispatch import (
|
|
24
24
|
TOOL_GOVERNANCE,
|
|
25
|
-
TOOL_GOVERNANCE_DEFAULT as _TOOL_GOVERNANCE_DEFAULT,
|
|
26
|
-
check_tool_role as _check_tool_role,
|
|
27
|
-
get_tool_permission,
|
|
28
25
|
enforce_tool_policy,
|
|
26
|
+
get_tool_permission,
|
|
29
27
|
list_tool_permissions,
|
|
30
28
|
tool_registry_diagnostics,
|
|
31
29
|
tool_registry_manifest,
|
|
32
30
|
)
|
|
33
|
-
from latticeai.services.
|
|
31
|
+
from latticeai.services.tool_dispatch import (
|
|
32
|
+
TOOL_GOVERNANCE_DEFAULT as _TOOL_GOVERNANCE_DEFAULT,
|
|
33
|
+
)
|
|
34
|
+
from latticeai.services.tool_dispatch import (
|
|
35
|
+
check_tool_role as _check_tool_role,
|
|
36
|
+
)
|
|
37
|
+
from latticeai.services.upload_service import process_uploaded_document
|
|
34
38
|
from latticeai.tools import (
|
|
35
39
|
AGENT_ROOT,
|
|
36
40
|
ToolError,
|
|
@@ -39,7 +43,6 @@ from latticeai.tools import (
|
|
|
39
43
|
create_pdf,
|
|
40
44
|
create_pptx,
|
|
41
45
|
create_xlsx,
|
|
42
|
-
read_document,
|
|
43
46
|
deploy_project,
|
|
44
47
|
edit_file,
|
|
45
48
|
git_diff,
|
|
@@ -49,8 +52,8 @@ from latticeai.tools import (
|
|
|
49
52
|
grep,
|
|
50
53
|
inspect_html,
|
|
51
54
|
knowledge_save,
|
|
52
|
-
knowledge_search,
|
|
53
55
|
knowledge_scope_root,
|
|
56
|
+
knowledge_search,
|
|
54
57
|
knowledge_tree,
|
|
55
58
|
list_dir,
|
|
56
59
|
network_status,
|
|
@@ -58,6 +61,7 @@ from latticeai.tools import (
|
|
|
58
61
|
obsidian_search,
|
|
59
62
|
obsidian_tree,
|
|
60
63
|
preview_url,
|
|
64
|
+
read_document,
|
|
61
65
|
read_file,
|
|
62
66
|
run_command,
|
|
63
67
|
search_files,
|
|
@@ -19,6 +19,8 @@ from typing import Any, Callable, Optional
|
|
|
19
19
|
|
|
20
20
|
from fastapi import APIRouter, File, Form, HTTPException, Request, UploadFile
|
|
21
21
|
|
|
22
|
+
from latticeai.core.quiet import quiet
|
|
23
|
+
|
|
22
24
|
LOGGER = logging.getLogger(__name__)
|
|
23
25
|
|
|
24
26
|
|
|
@@ -70,7 +72,7 @@ def create_voice_capture_router(
|
|
|
70
72
|
try:
|
|
71
73
|
tmp_path.unlink(missing_ok=True)
|
|
72
74
|
except OSError:
|
|
73
|
-
|
|
75
|
+
quiet()
|
|
74
76
|
if append_audit_event is not None:
|
|
75
77
|
try:
|
|
76
78
|
append_audit_event(
|
|
@@ -75,10 +75,10 @@ def create_workflow_designer_router(
|
|
|
75
75
|
from lattice_brain.workflow import (
|
|
76
76
|
WorkflowEngine,
|
|
77
77
|
WorkflowError,
|
|
78
|
-
validate_definition,
|
|
79
78
|
export_workflow,
|
|
80
79
|
import_workflow,
|
|
81
80
|
legacy_steps_from_nodes,
|
|
81
|
+
validate_definition,
|
|
82
82
|
)
|
|
83
83
|
|
|
84
84
|
router = APIRouter()
|
|
@@ -23,7 +23,6 @@ from pydantic import BaseModel
|
|
|
23
23
|
from latticeai.api.ui_redirects import app_redirect
|
|
24
24
|
from latticeai.services.app_context import AppContext
|
|
25
25
|
|
|
26
|
-
|
|
27
26
|
# ── Request models (workspace-only; moved verbatim from server_app) ──────────
|
|
28
27
|
|
|
29
28
|
class WorkspaceOnboardingStepRequest(BaseModel):
|
|
@@ -290,8 +289,8 @@ def create_workspace_router(context: AppContext) -> APIRouter:
|
|
|
290
289
|
# not_recommended) for this machine, used by the onboarding model step.
|
|
291
290
|
catalog = None
|
|
292
291
|
try:
|
|
293
|
-
from latticeai.setup.auto_setup import probe as auto_setup_probe
|
|
294
292
|
from latticeai.services.model_recommendation import recommend_catalog
|
|
293
|
+
from latticeai.setup.auto_setup import probe as auto_setup_probe
|
|
295
294
|
profile = await asyncio.to_thread(lambda: auto_setup_probe().to_json())
|
|
296
295
|
catalog = recommend_catalog(profile, engine="local_mlx")
|
|
297
296
|
except Exception as exc: # pragma: no cover - recommendation is best-effort
|