ltcai 8.1.0 → 8.3.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 +29 -48
- package/docs/CHANGELOG.md +70 -874
- package/docs/COMMUNITY_AND_PLUGINS.md +43 -0
- package/docs/DEVELOPMENT.md +26 -3
- package/docs/LEGACY_COMPATIBILITY.md +23 -1
- package/docs/ONBOARDING.md +38 -0
- package/docs/TRUST_MODEL.md +26 -26
- package/docs/WHY_LATTICE.md +29 -25
- package/docs/kg-schema.md +7 -5
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/graph/ingest.py +40 -8
- package/lattice_brain/graph/schema.py +6 -0
- package/lattice_brain/runtime/agent_runtime.py +22 -37
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/lattice_brain/workflow.py +26 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/computer_use.py +2 -0
- package/latticeai/api/knowledge_graph.py +33 -0
- package/latticeai/api/local_files.py +2 -0
- package/latticeai/api/memory.py +13 -0
- package/latticeai/api/tools.py +1 -0
- package/latticeai/api/workflow_designer.py +5 -4
- package/latticeai/app_factory.py +30 -28
- package/latticeai/brain/__init__.py +1 -1
- package/latticeai/core/config.py +10 -0
- package/latticeai/core/legacy_compatibility.py +174 -0
- package/latticeai/core/marketplace.py +1 -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 +37 -3
- package/latticeai/services/memory_service.py +215 -7
- package/latticeai/services/model_capability_registry.py +68 -0
- package/latticeai/services/model_engines.py +594 -0
- package/latticeai/services/model_loading.py +482 -0
- package/latticeai/services/model_recommendation.py +5 -0
- package/latticeai/services/model_runtime.py +131 -875
- package/latticeai/services/product_readiness.py +41 -18
- package/llm_router.py +7 -28
- package/mcp_registry.py +7 -24
- package/package.json +1 -1
- package/scripts/pts-claudecode-discord-bridge.mjs +2 -1
- package/src-tauri/Cargo.lock +1 -1
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/tauri.conf.json +1 -1
- package/static/app/asset-manifest.json +11 -11
- package/static/app/assets/{Act-BOO66G-c.js → Act-D5mo4tE4.js} +1 -1
- package/static/app/assets/{Brain-C6lEYiD7.js → Brain-BVWyQw8A.js} +1 -1
- package/static/app/assets/{Capture-TATXBRDw.js → Capture-C1R6GT0t.js} +1 -1
- package/static/app/assets/{Library-DK4FIp8a.js → Library-C2wIxpTs.js} +1 -1
- package/static/app/assets/{System-Bgs6Ql7x.js → System-DE5GRyQR.js} +1 -1
- package/static/app/assets/index-CoiuIFFP.js +16 -0
- package/static/app/assets/index-ty1iGgZu.css +2 -0
- package/static/app/assets/{primitives-B70WOra0.js → primitives-BdsUNXa6.js} +1 -1
- package/static/app/assets/{textarea-Czrd9gwM.js → textarea-e7qaj6Hm.js} +1 -1
- package/static/app/index.html +2 -2
- 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 -146
- 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/static/app/assets/index-Bvv79nre.js +0 -16
- package/static/app/assets/index-Dslqglia.css +0 -2
|
@@ -27,7 +27,7 @@ from dataclasses import dataclass, field
|
|
|
27
27
|
from datetime import datetime
|
|
28
28
|
from typing import Any, Callable, Dict, List, Optional
|
|
29
29
|
|
|
30
|
-
from lattice_brain.runtime.contracts import workflow_run_contract
|
|
30
|
+
from lattice_brain.runtime.contracts import runtime_boundary_contract, workflow_run_contract
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
WORKFLOW_ENGINE_VERSION = "2.2.0"
|
|
@@ -106,6 +106,11 @@ def normalize_definition(workflow: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
|
|
109
|
+
def legacy_steps_from_nodes(nodes: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
110
|
+
"""Return the compact legacy ``steps`` projection for node workflows."""
|
|
111
|
+
return [{"action": node.get("type"), "node": node.get("id")} for node in nodes]
|
|
112
|
+
|
|
113
|
+
|
|
109
114
|
def validate_definition(workflow: Dict[str, Any]) -> List[str]:
|
|
110
115
|
"""Return a list of validation errors ([] means valid)."""
|
|
111
116
|
errors: List[str] = []
|
|
@@ -269,6 +274,26 @@ class WorkflowEngine:
|
|
|
269
274
|
# against the workflow lifecycle actually executes.
|
|
270
275
|
self.hooks = hooks
|
|
271
276
|
|
|
277
|
+
def boundary(self) -> Dict[str, Any]:
|
|
278
|
+
return runtime_boundary_contract(
|
|
279
|
+
name="WorkflowEngine",
|
|
280
|
+
runtime="workflow",
|
|
281
|
+
entrypoint="lattice_brain.workflow.WorkflowEngine",
|
|
282
|
+
surface="/workflows",
|
|
283
|
+
owns="typed workflow validation, execution, approval suspension, resume, export, and import",
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
def config(self) -> Dict[str, Any]:
|
|
287
|
+
return {
|
|
288
|
+
"version": WORKFLOW_ENGINE_VERSION,
|
|
289
|
+
"boundary": self.boundary(),
|
|
290
|
+
"node_types": list(NODE_TYPES),
|
|
291
|
+
"runner_families": dict(_RUNNER_FOR),
|
|
292
|
+
"max_steps": _MAX_STEPS,
|
|
293
|
+
"approval_suspend": True,
|
|
294
|
+
"legacy_steps_projection": "lattice_brain.workflow.legacy_steps_from_nodes",
|
|
295
|
+
}
|
|
296
|
+
|
|
272
297
|
def run(self, workflow: Dict[str, Any], *, inputs: Optional[Dict[str, Any]] = None) -> WorkflowRun:
|
|
273
298
|
definition = normalize_definition(workflow)
|
|
274
299
|
errors = validate_definition(definition)
|
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.
|
|
@@ -13,6 +13,7 @@ from fastapi import APIRouter, HTTPException, Request
|
|
|
13
13
|
from pydantic import BaseModel
|
|
14
14
|
|
|
15
15
|
from latticeai.api.ui_redirects import app_redirect
|
|
16
|
+
from lattice_brain.ingestion import IngestionItem
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
class KnowledgeGraphIngestRequest(BaseModel):
|
|
@@ -61,6 +62,7 @@ def create_knowledge_graph_router(
|
|
|
61
62
|
require_user: Callable[[Request], str],
|
|
62
63
|
static_dir: Path,
|
|
63
64
|
allowed_workspaces_for: Optional[Callable[[Optional[str]], Any]] = None,
|
|
65
|
+
ingestion_pipeline: Any = None,
|
|
64
66
|
) -> APIRouter:
|
|
65
67
|
router = APIRouter()
|
|
66
68
|
|
|
@@ -191,6 +193,37 @@ def create_knowledge_graph_router(
|
|
|
191
193
|
if event_type not in {"message", "ai_response", "note"}:
|
|
192
194
|
raise HTTPException(status_code=400, detail="지원하는 type: message, ai_response, note")
|
|
193
195
|
role = req.role or ("assistant" if event_type == "ai_response" else "user")
|
|
196
|
+
if ingestion_pipeline is not None:
|
|
197
|
+
source_type = "chat_message" if event_type in {"message", "ai_response"} else "note"
|
|
198
|
+
result = ingestion_pipeline.ingest(
|
|
199
|
+
IngestionItem(
|
|
200
|
+
source_type=source_type,
|
|
201
|
+
title=req.title,
|
|
202
|
+
text=req.content,
|
|
203
|
+
source_uri=req.source,
|
|
204
|
+
owner=req.user_email or current_user,
|
|
205
|
+
workspace_id=workspace_id,
|
|
206
|
+
conversation_id=req.conversation_id,
|
|
207
|
+
metadata={
|
|
208
|
+
"type": req.type,
|
|
209
|
+
"role": role,
|
|
210
|
+
"source": req.source or "mcp",
|
|
211
|
+
"user_nickname": req.user_nickname,
|
|
212
|
+
"raw": {
|
|
213
|
+
"type": req.type,
|
|
214
|
+
"title": req.title,
|
|
215
|
+
"content": req.content,
|
|
216
|
+
"workspace_id": workspace_id,
|
|
217
|
+
"metadata": req.metadata or {},
|
|
218
|
+
},
|
|
219
|
+
**(req.metadata or {}),
|
|
220
|
+
},
|
|
221
|
+
),
|
|
222
|
+
user_email=req.user_email or current_user,
|
|
223
|
+
)
|
|
224
|
+
if result.status != "ok":
|
|
225
|
+
raise HTTPException(status_code=500, detail=result.detail or result.status)
|
|
226
|
+
return result.as_dict()
|
|
194
227
|
return kg.ingest_message(
|
|
195
228
|
role,
|
|
196
229
|
req.content,
|
|
@@ -46,6 +46,7 @@ def create_local_files_router(
|
|
|
46
46
|
require_graph,
|
|
47
47
|
static_dir: Path,
|
|
48
48
|
local_kg_watcher,
|
|
49
|
+
ingestion_pipeline=None,
|
|
49
50
|
hooks=None,
|
|
50
51
|
data_dir: Optional[Path] = None,
|
|
51
52
|
allowed_workspaces_for=None,
|
|
@@ -212,6 +213,7 @@ def create_local_files_router(
|
|
|
212
213
|
require_user=require_user,
|
|
213
214
|
static_dir=static_dir,
|
|
214
215
|
allowed_workspaces_for=allowed_workspaces_for,
|
|
216
|
+
ingestion_pipeline=ingestion_pipeline,
|
|
215
217
|
)
|
|
216
218
|
)
|
|
217
219
|
|
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/api/tools.py
CHANGED
|
@@ -488,6 +488,7 @@ def create_tools_router(
|
|
|
488
488
|
require_graph=_require_graph,
|
|
489
489
|
static_dir=STATIC_DIR,
|
|
490
490
|
local_kg_watcher=LOCAL_KG_WATCHER,
|
|
491
|
+
ingestion_pipeline=ingestion_pipeline,
|
|
491
492
|
hooks=HOOKS,
|
|
492
493
|
data_dir=DATA_DIR,
|
|
493
494
|
allowed_workspaces_for=allowed_workspaces_for,
|
|
@@ -73,10 +73,11 @@ def create_workflow_designer_router(
|
|
|
73
73
|
) -> APIRouter:
|
|
74
74
|
from lattice_brain.workflow import (
|
|
75
75
|
WorkflowEngine,
|
|
76
|
+
WorkflowError,
|
|
76
77
|
validate_definition,
|
|
77
78
|
export_workflow,
|
|
78
79
|
import_workflow,
|
|
79
|
-
|
|
80
|
+
legacy_steps_from_nodes,
|
|
80
81
|
)
|
|
81
82
|
|
|
82
83
|
router = APIRouter()
|
|
@@ -101,7 +102,7 @@ def create_workflow_designer_router(
|
|
|
101
102
|
raise HTTPException(status_code=400, detail={"validation_errors": errors})
|
|
102
103
|
workflow = store.create_workflow(
|
|
103
104
|
name=req.name,
|
|
104
|
-
steps=
|
|
105
|
+
steps=legacy_steps_from_nodes(req.nodes),
|
|
105
106
|
nodes=req.nodes,
|
|
106
107
|
metadata=req.metadata,
|
|
107
108
|
user_email=current_user or None,
|
|
@@ -293,7 +294,7 @@ def create_workflow_designer_router(
|
|
|
293
294
|
raise HTTPException(status_code=400, detail={"validation_errors": errors})
|
|
294
295
|
workflow = store.create_workflow(
|
|
295
296
|
name=definition["name"],
|
|
296
|
-
steps=
|
|
297
|
+
steps=legacy_steps_from_nodes(definition["nodes"]),
|
|
297
298
|
nodes=definition["nodes"],
|
|
298
299
|
metadata=definition["metadata"],
|
|
299
300
|
user_email=current_user or None,
|
|
@@ -343,7 +344,7 @@ def create_workflow_designer_router(
|
|
|
343
344
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
344
345
|
workflow = store.create_workflow(
|
|
345
346
|
name=definition["name"],
|
|
346
|
-
steps=
|
|
347
|
+
steps=legacy_steps_from_nodes(definition["nodes"]),
|
|
347
348
|
nodes=definition["nodes"],
|
|
348
349
|
metadata=definition.get("metadata") or {},
|
|
349
350
|
user_email=current_user or None,
|
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,
|
|
@@ -93,6 +95,7 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
93
95
|
|
|
94
96
|
from latticeai.models.router import LLMRouter, normalize_branding
|
|
95
97
|
from lattice_brain._kg_common import set_llm_router
|
|
98
|
+
from lattice_brain.graph.schema import set_embed_dim
|
|
96
99
|
from latticeai.core.security import (
|
|
97
100
|
hash_password,
|
|
98
101
|
verify_password,
|
|
@@ -105,7 +108,6 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
105
108
|
)
|
|
106
109
|
from latticeai.core.audit import (
|
|
107
110
|
get_audit_log as _get_audit_log,
|
|
108
|
-
append_audit_event as _append_audit_event,
|
|
109
111
|
classify_sensitive_message as _classify_sensitive_message,
|
|
110
112
|
build_sensitivity_report as _build_sensitivity_report,
|
|
111
113
|
build_admin_audit_report as _build_admin_audit_report,
|
|
@@ -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)
|
|
@@ -355,6 +340,7 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
355
340
|
extra={"target": CONFIG.embedding_custom_target},
|
|
356
341
|
probe=_embedding_provider not in {"", "hash", "local", "fallback"},
|
|
357
342
|
)
|
|
343
|
+
set_embed_dim(int(getattr(EMBEDDER, "dim", None) or _embedding_dim or 384))
|
|
358
344
|
if EMBEDDER.fell_back:
|
|
359
345
|
logging.warning("Embedding provider %s unavailable: %s", EMBEDDER.requested, EMBEDDER.detail)
|
|
360
346
|
STORAGE_ENGINE = storage_from_env(os.environ, data_dir=DATA_DIR) if ENABLE_GRAPH else None
|
|
@@ -479,6 +465,17 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
479
465
|
def get_sso_settings() -> Dict[str, object]:
|
|
480
466
|
return load_sso_config()
|
|
481
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
|
+
|
|
482
479
|
def public_sso_config(config: Optional[Dict[str, object]] = None) -> Dict[str, object]:
|
|
483
480
|
cfg = config or get_sso_settings()
|
|
484
481
|
return {
|
|
@@ -558,11 +555,7 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
558
555
|
|
|
559
556
|
_history_lock = threading.Lock()
|
|
560
557
|
|
|
561
|
-
|
|
562
|
-
return _get_audit_log(AUDIT_FILE)
|
|
563
|
-
|
|
564
|
-
def append_audit_event(event_type: str, **payload) -> None:
|
|
565
|
-
_append_audit_event(AUDIT_FILE, event_type, **payload)
|
|
558
|
+
# audit build moved after redact_secret_text is defined (see below)
|
|
566
559
|
|
|
567
560
|
def save_to_history(
|
|
568
561
|
role: str,
|
|
@@ -634,6 +627,15 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
634
627
|
def redact_secret_text(text: str) -> str:
|
|
635
628
|
return _redact_secret_text(text)
|
|
636
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
|
+
|
|
637
639
|
def get_history():
|
|
638
640
|
try:
|
|
639
641
|
return CONVERSATIONS.history()
|
package/latticeai/core/config.py
CHANGED
|
@@ -26,6 +26,8 @@ from typing import List, Mapping, Optional
|
|
|
26
26
|
from latticeai.core.security import host_is_loopback
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
__all__ = ["Config", "_value", "_str", "_bool", "_int"]
|
|
30
|
+
|
|
29
31
|
def _value(env: Mapping[str, str], key: str, default: str = "") -> str:
|
|
30
32
|
"""Mirror the legacy ``env_value``: ``getenv(key) or default or ""`` (no strip)."""
|
|
31
33
|
return env.get(key) or default or ""
|
|
@@ -68,6 +70,7 @@ class Config:
|
|
|
68
70
|
host: str
|
|
69
71
|
port: int
|
|
70
72
|
network_exposed: bool
|
|
73
|
+
timezone: str
|
|
71
74
|
|
|
72
75
|
# ── feature flags ───────────────────────────────────────────────
|
|
73
76
|
enable_telegram: bool
|
|
@@ -93,6 +96,9 @@ class Config:
|
|
|
93
96
|
local_model: str
|
|
94
97
|
local_draft_model: str
|
|
95
98
|
auto_read_chat_paths: bool
|
|
99
|
+
max_local_models: int
|
|
100
|
+
allow_model_downloads: bool
|
|
101
|
+
model_download_timeout: int
|
|
96
102
|
|
|
97
103
|
# ── embeddings (retrieval vector signal) ────────────────────────
|
|
98
104
|
embedding_provider: str
|
|
@@ -165,6 +171,7 @@ class Config:
|
|
|
165
171
|
host=host,
|
|
166
172
|
port=port,
|
|
167
173
|
network_exposed=network_exposed,
|
|
174
|
+
timezone=_value(env, "LATTICE_TZ", "UTC") or "UTC",
|
|
168
175
|
enable_telegram=_bool(env, "LATTICEAI_ENABLE_TELEGRAM", default=False),
|
|
169
176
|
enable_graph=_bool(env, "LATTICEAI_ENABLE_GRAPH", default=True),
|
|
170
177
|
autoload_models=_bool(env, "LATTICEAI_AUTOLOAD_MODELS", default=is_public),
|
|
@@ -184,6 +191,9 @@ class Config:
|
|
|
184
191
|
local_model=local_model,
|
|
185
192
|
local_draft_model=_value(env, "LATTICEAI_LOCAL_DRAFT_MODEL", ""),
|
|
186
193
|
auto_read_chat_paths=_bool(env, "LATTICEAI_AUTO_READ_CHAT_PATHS", default=False),
|
|
194
|
+
max_local_models=_int(env, "LATTICEAI_MAX_LOCAL_MODELS", 1),
|
|
195
|
+
allow_model_downloads=_bool(env, "LATTICEAI_ALLOW_MODEL_DOWNLOADS", default=False),
|
|
196
|
+
model_download_timeout=_int(env, "LATTICEAI_MODEL_DOWNLOAD_TIMEOUT", 300),
|
|
187
197
|
embedding_provider=_value(env, "LATTICEAI_EMBEDDING_PROVIDER", "hash").strip().lower(),
|
|
188
198
|
embedding_profile=_value(env, "LATTICEAI_EMBEDDING_PROFILE", "").strip().lower(),
|
|
189
199
|
embedding_model=_value(env, "LATTICEAI_EMBEDDING_MODEL", ""),
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""Managed legacy compatibility surface for legacy import shims.
|
|
2
|
+
|
|
3
|
+
Compatibility modules are intentionally still present for old scripts, VS Code
|
|
4
|
+
extension paths, historical integrations, and pre-graph-package imports. 8.3.0
|
|
5
|
+
stops treating them as vague technical debt: every tracked shim has an owner,
|
|
6
|
+
migration target, removal phase, and replacement import that can be surfaced in
|
|
7
|
+
docs and tests.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any, Dict, List
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
LEGACY_COMPATIBILITY_VERSION = "8.3.0"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True)
|
|
21
|
+
class LegacyShim:
|
|
22
|
+
path: str
|
|
23
|
+
owner: str
|
|
24
|
+
replacement: str
|
|
25
|
+
reason: str
|
|
26
|
+
removal_phase: str
|
|
27
|
+
layer: str = "root"
|
|
28
|
+
status: str = "managed"
|
|
29
|
+
|
|
30
|
+
def as_dict(self) -> Dict[str, str]:
|
|
31
|
+
return {
|
|
32
|
+
"path": self.path,
|
|
33
|
+
"owner": self.owner,
|
|
34
|
+
"replacement": self.replacement,
|
|
35
|
+
"reason": self.reason,
|
|
36
|
+
"removal_phase": self.removal_phase,
|
|
37
|
+
"layer": self.layer,
|
|
38
|
+
"status": self.status,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
LEGACY_SHIMS: List[LegacyShim] = [
|
|
43
|
+
LegacyShim(
|
|
44
|
+
path="knowledge_graph.py",
|
|
45
|
+
owner="lattice_brain.graph",
|
|
46
|
+
replacement="from lattice_brain.store import KnowledgeGraphStore",
|
|
47
|
+
reason="Historical scripts imported the graph store from the repo root.",
|
|
48
|
+
removal_phase="major-release-after-8.x",
|
|
49
|
+
),
|
|
50
|
+
LegacyShim(
|
|
51
|
+
path="knowledge_graph_api.py",
|
|
52
|
+
owner="latticeai.api.knowledge_graph",
|
|
53
|
+
replacement="from latticeai.api.knowledge_graph import create_knowledge_graph_router",
|
|
54
|
+
reason="Older API composition roots imported the knowledge graph router from the repo root.",
|
|
55
|
+
removal_phase="major-release-after-8.x",
|
|
56
|
+
),
|
|
57
|
+
LegacyShim(
|
|
58
|
+
path="kg_schema.py",
|
|
59
|
+
owner="lattice_brain.graph.schema",
|
|
60
|
+
replacement="from lattice_brain.schema import ...",
|
|
61
|
+
reason="Historical graph schema tests and tools referenced root schema symbols.",
|
|
62
|
+
removal_phase="major-release-after-8.x",
|
|
63
|
+
),
|
|
64
|
+
LegacyShim(
|
|
65
|
+
path="ltcai_cli.py",
|
|
66
|
+
owner="latticeai.cli.entrypoint",
|
|
67
|
+
replacement="from latticeai.cli.entrypoint import main",
|
|
68
|
+
reason="Console entry points and older package installs imported the CLI root module.",
|
|
69
|
+
removal_phase="major-release-after-8.x",
|
|
70
|
+
),
|
|
71
|
+
LegacyShim(
|
|
72
|
+
path="telegram_bot.py",
|
|
73
|
+
owner="latticeai.integrations.telegram_bot",
|
|
74
|
+
replacement="from latticeai.integrations.telegram_bot import run_bot",
|
|
75
|
+
reason="Existing Telegram automation setups still import the legacy root module.",
|
|
76
|
+
removal_phase="major-release-after-8.x",
|
|
77
|
+
),
|
|
78
|
+
LegacyShim(
|
|
79
|
+
path="p_reinforce.py",
|
|
80
|
+
owner="latticeai.services.p_reinforce",
|
|
81
|
+
replacement="from latticeai.services.p_reinforce import PReinforceGardener",
|
|
82
|
+
reason="Old gardener scripts referenced the root reinforcement helper.",
|
|
83
|
+
removal_phase="major-release-after-8.x",
|
|
84
|
+
),
|
|
85
|
+
LegacyShim(
|
|
86
|
+
path="server.py",
|
|
87
|
+
owner="latticeai.server_app",
|
|
88
|
+
replacement="from latticeai.server_app import app, main",
|
|
89
|
+
reason="Deployment docs and local launch scripts historically targeted server.py.",
|
|
90
|
+
removal_phase="major-release-after-8.x",
|
|
91
|
+
),
|
|
92
|
+
LegacyShim(
|
|
93
|
+
path="local_knowledge_api.py",
|
|
94
|
+
owner="latticeai.api.local_files",
|
|
95
|
+
replacement="from latticeai.api.local_files import create_local_files_router",
|
|
96
|
+
reason="Local folder ingestion integrations used the root local knowledge API.",
|
|
97
|
+
removal_phase="requires-api-route-migration",
|
|
98
|
+
),
|
|
99
|
+
LegacyShim(
|
|
100
|
+
path="lattice_brain/store.py",
|
|
101
|
+
owner="lattice_brain.graph.store",
|
|
102
|
+
replacement="from lattice_brain.graph.store import KnowledgeGraphStore",
|
|
103
|
+
reason="Pre-graph-package imports used the flat Brain store module.",
|
|
104
|
+
removal_phase="major-release-after-8.x",
|
|
105
|
+
layer="brain-flat",
|
|
106
|
+
),
|
|
107
|
+
LegacyShim(
|
|
108
|
+
path="lattice_brain/ingest.py",
|
|
109
|
+
owner="lattice_brain.graph.ingest",
|
|
110
|
+
replacement="from lattice_brain.graph.ingest import KnowledgeGraphIngestMixin",
|
|
111
|
+
reason="Pre-graph-package imports used the flat Brain ingest module.",
|
|
112
|
+
removal_phase="major-release-after-8.x",
|
|
113
|
+
layer="brain-flat",
|
|
114
|
+
),
|
|
115
|
+
LegacyShim(
|
|
116
|
+
path="lattice_brain/retrieval.py",
|
|
117
|
+
owner="lattice_brain.graph.retrieval",
|
|
118
|
+
replacement="from lattice_brain.graph.retrieval import KnowledgeGraphRetrievalMixin",
|
|
119
|
+
reason="Pre-graph-package imports used the flat Brain retrieval module.",
|
|
120
|
+
removal_phase="major-release-after-8.x",
|
|
121
|
+
layer="brain-flat",
|
|
122
|
+
),
|
|
123
|
+
LegacyShim(
|
|
124
|
+
path="latticeai/brain/store.py",
|
|
125
|
+
owner="lattice_brain.graph.store",
|
|
126
|
+
replacement="from lattice_brain.graph.store import KnowledgeGraphStore",
|
|
127
|
+
reason="The deprecated latticeai.brain namespace remains for package users.",
|
|
128
|
+
removal_phase="major-release-after-8.x",
|
|
129
|
+
layer="deprecated-namespace",
|
|
130
|
+
),
|
|
131
|
+
LegacyShim(
|
|
132
|
+
path="latticeai/brain/ingest.py",
|
|
133
|
+
owner="lattice_brain.graph.ingest",
|
|
134
|
+
replacement="from lattice_brain.graph.ingest import KnowledgeGraphIngestMixin",
|
|
135
|
+
reason="The deprecated latticeai.brain namespace remains for package users.",
|
|
136
|
+
removal_phase="major-release-after-8.x",
|
|
137
|
+
layer="deprecated-namespace",
|
|
138
|
+
),
|
|
139
|
+
LegacyShim(
|
|
140
|
+
path="latticeai/services/agent_runtime.py",
|
|
141
|
+
owner="lattice_brain.runtime.agent_runtime",
|
|
142
|
+
replacement="from lattice_brain.runtime.agent_runtime import AgentRuntime",
|
|
143
|
+
reason="Service-layer imports existed before AgentRuntime moved into Brain runtime.",
|
|
144
|
+
removal_phase="major-release-after-8.x",
|
|
145
|
+
layer="service-alias",
|
|
146
|
+
),
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def legacy_shim_report(root: Path | None = None) -> Dict[str, Any]:
|
|
151
|
+
if root is None:
|
|
152
|
+
root = Path(__file__).resolve().parents[2]
|
|
153
|
+
entries = [shim.as_dict() for shim in LEGACY_SHIMS]
|
|
154
|
+
missing = [shim.path for shim in LEGACY_SHIMS if not (root / shim.path).exists()]
|
|
155
|
+
phases = sorted({shim.removal_phase for shim in LEGACY_SHIMS})
|
|
156
|
+
layers = sorted({shim.layer for shim in LEGACY_SHIMS})
|
|
157
|
+
return {
|
|
158
|
+
"schema_version": "legacy-compatibility/v1",
|
|
159
|
+
"version_target": LEGACY_COMPATIBILITY_VERSION,
|
|
160
|
+
"status": "managed" if not missing else "incomplete",
|
|
161
|
+
"remaining_count": len(LEGACY_SHIMS),
|
|
162
|
+
"missing": missing,
|
|
163
|
+
"removal_phases": phases,
|
|
164
|
+
"layers": layers,
|
|
165
|
+
"shims": entries,
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
__all__ = [
|
|
170
|
+
"LEGACY_COMPATIBILITY_VERSION",
|
|
171
|
+
"LEGACY_SHIMS",
|
|
172
|
+
"LegacyShim",
|
|
173
|
+
"legacy_shim_report",
|
|
174
|
+
]
|
|
@@ -9,6 +9,16 @@ effects at import time.
|
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
+
__all__ = [
|
|
13
|
+
"ToolPolicy",
|
|
14
|
+
"ToolPermission",
|
|
15
|
+
"TOOL_CATALOG_BRIEF",
|
|
16
|
+
"FILE_CREATE_ACTIONS",
|
|
17
|
+
"LOCAL_WRITE_BLOCKED_PREFIXES",
|
|
18
|
+
"RISK_LEVEL_MAP",
|
|
19
|
+
"ToolRegistry",
|
|
20
|
+
]
|
|
21
|
+
|
|
12
22
|
from dataclasses import dataclass, field
|
|
13
23
|
from typing import Any, Callable, Dict, Mapping, Optional, TypedDict
|
|
14
24
|
|