ltcai 4.0.0 → 4.0.1
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 +37 -33
- package/docs/CHANGELOG.md +64 -0
- package/docs/REALTIME_COLLABORATION.md +3 -3
- package/docs/V3_FRONTEND.md +9 -8
- package/docs/V4_DIGITAL_BRAIN_RECOVERY.md +86 -43
- package/docs/kg-schema.md +6 -2
- package/docs/spec-vs-impl.md +10 -10
- package/kg_schema.py +2 -603
- package/knowledge_graph.py +37 -4958
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/admin.py +15 -16
- package/latticeai/api/agents.py +13 -6
- package/latticeai/api/auth.py +19 -11
- package/latticeai/api/invitations.py +100 -0
- package/latticeai/api/knowledge_graph.py +4 -11
- package/latticeai/api/plugins.py +3 -6
- package/latticeai/api/realtime.py +4 -7
- package/latticeai/api/static_routes.py +9 -12
- package/latticeai/api/ui_redirects.py +26 -0
- package/latticeai/api/workflow_designer.py +39 -6
- package/latticeai/api/workspace.py +24 -10
- package/latticeai/app_factory.py +88 -17
- package/latticeai/brain/_kg_common.py +1123 -0
- package/latticeai/brain/discovery.py +1455 -0
- package/latticeai/brain/documents.py +218 -0
- package/latticeai/brain/ingest.py +644 -0
- package/latticeai/brain/projection.py +561 -0
- package/latticeai/brain/provenance.py +401 -0
- package/latticeai/brain/retrieval.py +1316 -0
- package/latticeai/brain/schema.py +640 -0
- package/latticeai/brain/store.py +216 -0
- package/latticeai/brain/write_master.py +225 -0
- package/latticeai/core/invitations.py +131 -0
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/multi_agent.py +1 -1
- package/latticeai/core/policy.py +54 -0
- package/latticeai/core/realtime.py +65 -44
- package/latticeai/core/sessions.py +31 -5
- package/latticeai/core/users.py +147 -0
- package/latticeai/core/workspace_os.py +420 -20
- package/latticeai/services/agent_runtime.py +242 -4
- package/latticeai/services/run_executor.py +328 -0
- package/latticeai/services/workspace_service.py +27 -19
- package/package.json +2 -14
- package/scripts/lint_v3.mjs +23 -0
- package/static/v3/asset-manifest.json +21 -14
- package/static/v3/js/{app.356e6452.js → app.c5c80c46.js} +1 -1
- package/static/v3/js/core/{api.7a308b89.js → api.ba0fbf14.js} +58 -1
- package/static/v3/js/core/api.js +57 -0
- package/static/v3/js/core/i18n.880e1fec.js +575 -0
- package/static/v3/js/core/i18n.js +575 -0
- package/static/v3/js/core/routes.37522821.js +101 -0
- package/static/v3/js/core/routes.js +71 -63
- package/static/v3/js/core/{shell.a1657f20.js → shell.e3f6bbfa.js} +67 -38
- package/static/v3/js/core/shell.js +65 -36
- package/static/v3/js/core/{store.204a08b2.js → store.7b2aa044.js} +10 -0
- package/static/v3/js/core/store.js +10 -0
- package/static/v3/js/views/account.eff40715.js +143 -0
- package/static/v3/js/views/account.js +143 -0
- package/static/v3/js/views/activity.0d271ef9.js +67 -0
- package/static/v3/js/views/activity.js +67 -0
- package/static/v3/js/views/{admin-users.03bac88c.js → admin-users.f7ac7b43.js} +4 -6
- package/static/v3/js/views/admin-users.js +4 -6
- package/static/v3/js/views/{agents.014d0b74.js → agents.17c5288d.js} +35 -12
- package/static/v3/js/views/agents.js +35 -12
- package/static/v3/js/views/{chat.e6dd7dd0.js → chat.e250e2cc.js} +23 -0
- package/static/v3/js/views/chat.js +23 -0
- package/static/v3/js/views/{knowledge-graph.5e40cbeb.js → knowledge-graph.4d09c537.js} +27 -7
- package/static/v3/js/views/knowledge-graph.js +27 -7
- package/static/v3/js/views/network.52a4f181.js +97 -0
- package/static/v3/js/views/network.js +97 -0
- package/static/v3/js/views/{planning.9ac3e313.js → planning.4876fd77.js} +26 -5
- package/static/v3/js/views/planning.js +26 -5
- package/static/v3/js/views/runs.b63b2afa.js +144 -0
- package/static/v3/js/views/runs.js +144 -0
- package/static/v3/js/views/{settings.8631fa5e.js → settings.b7140634.js} +7 -8
- package/static/v3/js/views/settings.js +7 -8
- package/static/v3/js/views/snapshots.6f5db095.js +135 -0
- package/static/v3/js/views/snapshots.js +135 -0
- package/static/v3/js/views/{workflows.26c57290.js → workflows.7752225a.js} +87 -2
- package/static/v3/js/views/workflows.js +87 -2
- package/static/v3/js/views/workspace-admin.c466029b.js +156 -0
- package/static/v3/js/views/workspace-admin.js +156 -0
- package/static/account.html +0 -113
- package/static/activity.html +0 -73
- package/static/admin.html +0 -486
- package/static/agents.html +0 -139
- package/static/chat.html +0 -841
- package/static/css/reference/account.css +0 -439
- package/static/css/reference/admin.css +0 -610
- package/static/css/reference/base.css +0 -1661
- package/static/css/reference/chat.css +0 -4623
- package/static/css/reference/graph.css +0 -1016
- package/static/css/responsive.css +0 -861
- package/static/graph.html +0 -122
- package/static/platform.css +0 -104
- package/static/plugins.html +0 -136
- package/static/scripts/account.js +0 -238
- package/static/scripts/admin.js +0 -1614
- package/static/scripts/chat.js +0 -5081
- package/static/scripts/graph.js +0 -1804
- package/static/scripts/platform.js +0 -64
- package/static/scripts/ux.js +0 -167
- package/static/scripts/workspace.js +0 -948
- package/static/v3/js/core/routes.7222343d.js +0 -93
- package/static/workflows.html +0 -146
- package/static/workspace.css +0 -1121
- package/static/workspace.html +0 -357
package/latticeai/app_factory.py
CHANGED
|
@@ -94,6 +94,16 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
94
94
|
from latticeai.core.enterprise import (
|
|
95
95
|
capability_registry,
|
|
96
96
|
)
|
|
97
|
+
from latticeai.core.invitations import InvitationStore
|
|
98
|
+
from latticeai.core.policy import normalize_role, policy_matrix, require_capability
|
|
99
|
+
from latticeai.core.users import (
|
|
100
|
+
ensure_user_identity,
|
|
101
|
+
load_users_file,
|
|
102
|
+
migrate_knowledge_graph_identity,
|
|
103
|
+
normalize_email,
|
|
104
|
+
save_users_file,
|
|
105
|
+
user_id_for_email as _user_id_for_email,
|
|
106
|
+
)
|
|
97
107
|
from latticeai.services.app_context import AppContext
|
|
98
108
|
from latticeai.services.workspace_service import WorkspaceService
|
|
99
109
|
from latticeai.services.model_service import ModelService
|
|
@@ -127,10 +137,12 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
127
137
|
from latticeai.core.realtime import RealtimeBus
|
|
128
138
|
from latticeai.core.marketplace import TemplateCatalog
|
|
129
139
|
from latticeai.services.platform_runtime import PlatformRuntime
|
|
140
|
+
from latticeai.services.run_executor import RunExecutor
|
|
130
141
|
from latticeai.api.plugins import create_plugins_router
|
|
131
142
|
from latticeai.api.workflow_designer import create_workflow_designer_router
|
|
132
143
|
from latticeai.api.agents import create_agents_router
|
|
133
144
|
from latticeai.api.realtime import create_realtime_router
|
|
145
|
+
from latticeai.api.invitations import create_invitations_router
|
|
134
146
|
from latticeai.api.marketplace import create_marketplace_router
|
|
135
147
|
from latticeai.api.models import create_models_router
|
|
136
148
|
from latticeai.api.chat import create_chat_router
|
|
@@ -277,12 +289,18 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
277
289
|
def _client_ip(request: Request) -> str:
|
|
278
290
|
return _client_ip_impl(request)
|
|
279
291
|
|
|
292
|
+
def user_id_for_email(email: Optional[str]) -> Optional[str]:
|
|
293
|
+
return _user_id_for_email(load_users(), email)
|
|
294
|
+
|
|
280
295
|
def create_session(email: str) -> str:
|
|
281
|
-
return _session_store.create(email)
|
|
296
|
+
return _session_store.create(user_id_for_email(email) or email, email=email)
|
|
282
297
|
|
|
283
298
|
def get_session_email(token: str) -> Optional[str]:
|
|
284
299
|
return _session_store.get_email(token)
|
|
285
300
|
|
|
301
|
+
def get_session_user_id(token: str) -> Optional[str]:
|
|
302
|
+
return _session_store.get_subject(token)
|
|
303
|
+
|
|
286
304
|
def invalidate_session(token: str) -> None:
|
|
287
305
|
_session_store.invalidate(token)
|
|
288
306
|
|
|
@@ -345,7 +363,8 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
345
363
|
WORKSPACE_OS = WorkspaceOSStore(DATA_DIR, event_sink=REALTIME_BUS)
|
|
346
364
|
# Service layer (latticeai.services) wraps the store with scope/permission
|
|
347
365
|
# guardrails; routers and the app assembly share this single instance.
|
|
348
|
-
WORKSPACE_SERVICE = WorkspaceService(WORKSPACE_OS)
|
|
366
|
+
WORKSPACE_SERVICE = WorkspaceService(WORKSPACE_OS, resolve_user_id=user_id_for_email)
|
|
367
|
+
INVITATION_STORE = InvitationStore(DATA_DIR / "invitations.json")
|
|
349
368
|
# ── v2 Plugin SDK registry (extends skills; discovers plugins/<id>/plugin.json)
|
|
350
369
|
PLUGINS_DIR = Path(os.getenv("LATTICEAI_PLUGINS_DIR") or (BASE_DIR / "plugins"))
|
|
351
370
|
PLUGIN_REGISTRY = PluginRegistry(PLUGINS_DIR, store=WORKSPACE_OS)
|
|
@@ -496,14 +515,24 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
496
515
|
|
|
497
516
|
|
|
498
517
|
def load_users():
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
518
|
+
users = load_users_file(USERS_FILE)
|
|
519
|
+
email_to_id = {
|
|
520
|
+
email: user.get("id")
|
|
521
|
+
for email, user in users.items()
|
|
522
|
+
if isinstance(user, dict) and user.get("id")
|
|
523
|
+
}
|
|
524
|
+
try:
|
|
525
|
+
migrate_knowledge_graph_identity(DATA_DIR / "knowledge_graph.sqlite", email_to_id)
|
|
526
|
+
except Exception as exc:
|
|
527
|
+
logging.warning("knowledge graph identity migration skipped: %s", exc)
|
|
528
|
+
try:
|
|
529
|
+
WORKSPACE_OS.migrate_workspace_identities(email_to_id)
|
|
530
|
+
except Exception as exc:
|
|
531
|
+
logging.warning("workspace identity migration skipped: %s", exc)
|
|
532
|
+
return users
|
|
503
533
|
|
|
504
534
|
def save_users(users):
|
|
505
|
-
|
|
506
|
-
json.dump(users, f, ensure_ascii=False, indent=2)
|
|
535
|
+
save_users_file(USERS_FILE, users)
|
|
507
536
|
|
|
508
537
|
def load_vpc_config() -> Dict:
|
|
509
538
|
if not os.path.exists(VPC_FILE):
|
|
@@ -786,14 +815,22 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
786
815
|
|
|
787
816
|
def get_user_role(email: str, users: Optional[Dict] = None) -> str:
|
|
788
817
|
users = users or load_users()
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
818
|
+
identity = str(email or "")
|
|
819
|
+
normalized_email = normalize_email(identity)
|
|
820
|
+
user = users.get(normalized_email) or users.get(identity) or next(
|
|
821
|
+
(
|
|
822
|
+
item for item in users.values()
|
|
823
|
+
if isinstance(item, dict) and item.get("id") == identity
|
|
824
|
+
),
|
|
825
|
+
{},
|
|
826
|
+
)
|
|
827
|
+
if isinstance(user, dict) and user.get("role"):
|
|
828
|
+
return normalize_role(user["role"])
|
|
829
|
+
admin_emails = {normalize_email(item) for item in CONFIG.admin_emails}
|
|
830
|
+
if normalized_email in admin_emails:
|
|
794
831
|
return "admin"
|
|
795
832
|
first_email = next(iter(users), None)
|
|
796
|
-
return "admin" if first_email ==
|
|
833
|
+
return "admin" if first_email == normalized_email else "user"
|
|
797
834
|
|
|
798
835
|
def _extract_bearer_token(request: Request) -> Optional[str]:
|
|
799
836
|
auth = request.headers.get("Authorization", "")
|
|
@@ -888,16 +925,24 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
888
925
|
if token:
|
|
889
926
|
email = get_session_email(token)
|
|
890
927
|
if email:
|
|
891
|
-
|
|
928
|
+
role = get_user_role(email, users)
|
|
929
|
+
try:
|
|
930
|
+
require_capability(role, "admin:users")
|
|
892
931
|
return email, users
|
|
932
|
+
except PermissionError:
|
|
933
|
+
pass
|
|
893
934
|
raise HTTPException(status_code=403, detail="관리자 권한이 필요합니다.")
|
|
894
935
|
|
|
895
936
|
def public_user(email: str, user: Dict, users: Dict) -> Dict:
|
|
937
|
+
role = get_user_role(email, users)
|
|
938
|
+
user_id = user.get("id") or _user_id_for_email(users, email)
|
|
896
939
|
return {
|
|
940
|
+
"id": user_id,
|
|
897
941
|
"email": email,
|
|
942
|
+
"identity": user_id,
|
|
898
943
|
"name": user.get("name", ""),
|
|
899
944
|
"nickname": user.get("nickname", ""),
|
|
900
|
-
"role":
|
|
945
|
+
"role": role,
|
|
901
946
|
"disabled": bool(user.get("disabled", False)),
|
|
902
947
|
}
|
|
903
948
|
|
|
@@ -968,6 +1013,7 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
968
1013
|
"role": "user",
|
|
969
1014
|
"disabled": False,
|
|
970
1015
|
}
|
|
1016
|
+
ensure_user_identity(email, user)
|
|
971
1017
|
api_keys = user.get("api_keys") or {}
|
|
972
1018
|
api_keys[provider] = key
|
|
973
1019
|
user["api_keys"] = api_keys
|
|
@@ -1194,6 +1240,7 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
1194
1240
|
public_sso_config=public_sso_config,
|
|
1195
1241
|
open_registration=OPEN_REGISTRATION, session_ttl=_SESSION_TTL,
|
|
1196
1242
|
require_auth=REQUIRE_AUTH,
|
|
1243
|
+
ensure_identity=ensure_user_identity,
|
|
1197
1244
|
))
|
|
1198
1245
|
|
|
1199
1246
|
def _graph_stats_safe():
|
|
@@ -1215,6 +1262,16 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
1215
1262
|
get_graph_stats=_graph_stats_safe, enable_graph=ENABLE_GRAPH,
|
|
1216
1263
|
invite_code=INVITE_CODE, invite_gate_enabled=INVITE_GATE_ENABLED,
|
|
1217
1264
|
default_port=DEFAULT_PORT,
|
|
1265
|
+
policy_matrix=policy_matrix,
|
|
1266
|
+
))
|
|
1267
|
+
|
|
1268
|
+
app.include_router(create_invitations_router(
|
|
1269
|
+
invitation_store=INVITATION_STORE,
|
|
1270
|
+
workspace_service=WORKSPACE_SERVICE,
|
|
1271
|
+
require_admin=require_admin,
|
|
1272
|
+
require_user=require_user,
|
|
1273
|
+
user_id_for_email=user_id_for_email,
|
|
1274
|
+
append_audit_event=append_audit_event,
|
|
1218
1275
|
))
|
|
1219
1276
|
|
|
1220
1277
|
# ── Security & Audit Command Center (피드백 #5) ──────────────────────────────
|
|
@@ -1426,7 +1483,6 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
1426
1483
|
description="Fires brain_event workflow triggers when knowledge enters the brain.",
|
|
1427
1484
|
)["id"]
|
|
1428
1485
|
HOOKS_REGISTRY.register_hook(_trigger_hook_id, TRIGGER_SERVICE.hook_runner())
|
|
1429
|
-
TRIGGER_SERVICE.start()
|
|
1430
1486
|
|
|
1431
1487
|
# Single AgentRuntime boundary over the orchestrator + run store.
|
|
1432
1488
|
AGENT_RUNTIME = AgentRuntime(
|
|
@@ -1436,6 +1492,18 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
1436
1492
|
append_audit_event=append_audit_event,
|
|
1437
1493
|
hooks=HOOKS_REGISTRY,
|
|
1438
1494
|
)
|
|
1495
|
+
RUN_EXECUTOR = RunExecutor(
|
|
1496
|
+
store=WORKSPACE_OS,
|
|
1497
|
+
agent_runtime=AGENT_RUNTIME,
|
|
1498
|
+
build_workflow_runners=PLATFORM.build_workflow_runners,
|
|
1499
|
+
workspace_graph=_workspace_graph,
|
|
1500
|
+
append_audit_event=append_audit_event,
|
|
1501
|
+
hooks=HOOKS_REGISTRY,
|
|
1502
|
+
)
|
|
1503
|
+
AGENT_RUNTIME.attach_executor(RUN_EXECUTOR)
|
|
1504
|
+
app.state.run_executor = RUN_EXECUTOR
|
|
1505
|
+
app.state.run_reconciliation = RUN_EXECUTOR.reconcile_startup()
|
|
1506
|
+
TRIGGER_SERVICE.start()
|
|
1439
1507
|
|
|
1440
1508
|
# ── Hooks dispatch: bind real built-in runners ───────────────────────────────
|
|
1441
1509
|
# The registry lists built-in hooks; binding a runner here makes them *execute*
|
|
@@ -1472,6 +1540,8 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
1472
1540
|
ui_file_response=ui_file_response,
|
|
1473
1541
|
static_dir=STATIC_DIR,
|
|
1474
1542
|
hooks=HOOKS_REGISTRY,
|
|
1543
|
+
run_executor=RUN_EXECUTOR,
|
|
1544
|
+
trigger_service=TRIGGER_SERVICE,
|
|
1475
1545
|
))
|
|
1476
1546
|
|
|
1477
1547
|
app.include_router(create_agents_router(
|
|
@@ -1486,6 +1556,7 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
|
|
|
1486
1556
|
ui_file_response=ui_file_response,
|
|
1487
1557
|
static_dir=STATIC_DIR,
|
|
1488
1558
|
agent_runtime=AGENT_RUNTIME,
|
|
1559
|
+
run_executor=RUN_EXECUTOR,
|
|
1489
1560
|
))
|
|
1490
1561
|
|
|
1491
1562
|
app.include_router(create_marketplace_router(
|