ltcai 5.6.0 → 6.0.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 +42 -25
- package/docs/CHANGELOG.md +38 -0
- package/frontend/openapi.json +39 -0
- package/frontend/src/api/client.ts +104 -23
- package/frontend/src/api/openapi.ts +48 -0
- package/frontend/src/components/FirstRunGuide.tsx +3 -3
- package/frontend/src/features/review/ReviewCard.tsx +91 -0
- package/frontend/src/features/review/ReviewInbox.tsx +112 -0
- package/frontend/src/features/review/reviewHelpers.ts +69 -0
- package/frontend/src/i18n.ts +8 -8
- package/frontend/src/pages/Act.tsx +5 -177
- package/frontend/src/routes.ts +1 -0
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/review_queue.py +7 -3
- package/latticeai/app_factory.py +224 -473
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/runtime/app_context_runtime.py +13 -0
- package/latticeai/runtime/automation_runtime.py +64 -0
- package/latticeai/runtime/bootstrap.py +48 -0
- package/latticeai/runtime/context_runtime.py +43 -0
- package/latticeai/runtime/hooks_runtime.py +77 -0
- package/latticeai/runtime/lifespan_runtime.py +138 -0
- package/latticeai/runtime/persistence_runtime.py +87 -0
- package/latticeai/runtime/platform_services_runtime.py +39 -0
- package/latticeai/runtime/router_registration.py +570 -0
- package/latticeai/runtime/web_runtime.py +65 -0
- package/latticeai/services/review_queue.py +20 -4
- package/package.json +1 -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 +3 -3
- package/static/app/assets/index-D2zafMYb.js +16 -0
- package/static/app/assets/index-D2zafMYb.js.map +1 -0
- package/static/app/index.html +1 -1
- package/static/app/assets/index-xMFu94cX.js +0 -16
- package/static/app/assets/index-xMFu94cX.js.map +0 -1
|
@@ -19,7 +19,7 @@ from pathlib import Path
|
|
|
19
19
|
from typing import Any, Callable, Dict, Iterable, List, Optional
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
WORKSPACE_OS_VERSION = "
|
|
22
|
+
WORKSPACE_OS_VERSION = "6.0.0"
|
|
23
23
|
|
|
24
24
|
# Workspace types separate single-user Personal workspaces from shared
|
|
25
25
|
# Organization workspaces. Both keep the same local-first JSON store; the type
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Application dependency context assembly for app startup."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def build_app_context(**deps: Any) -> Any:
|
|
9
|
+
"""Construct the typed dependency context consumed by API routers."""
|
|
10
|
+
|
|
11
|
+
from latticeai.services.app_context import AppContext
|
|
12
|
+
|
|
13
|
+
return AppContext(**deps)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Automation runtime assembly for review, trigger, agent, and run execution.
|
|
2
|
+
|
|
3
|
+
This module keeps the automation object graph behind one construction seam.
|
|
4
|
+
Router registration remains in ``app_factory`` so route order stays unchanged.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any, Callable, Dict
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def build_automation_runtime(
|
|
13
|
+
*,
|
|
14
|
+
store: Any,
|
|
15
|
+
platform: Any,
|
|
16
|
+
data_dir: Any,
|
|
17
|
+
workspace_graph: Callable[..., Any],
|
|
18
|
+
append_audit_event: Callable[..., Any],
|
|
19
|
+
hooks: Any,
|
|
20
|
+
) -> Dict[str, Any]:
|
|
21
|
+
"""Construct automation services and attach the run executor boundary."""
|
|
22
|
+
|
|
23
|
+
from lattice_brain.runtime.agent_runtime import AgentRuntime
|
|
24
|
+
from latticeai.services.review_queue import ReviewQueueService
|
|
25
|
+
from latticeai.services.run_executor import RunExecutor
|
|
26
|
+
from latticeai.services.triggers import TriggerService
|
|
27
|
+
|
|
28
|
+
review_queue = ReviewQueueService(store=store)
|
|
29
|
+
trigger_service = TriggerService(
|
|
30
|
+
store=store,
|
|
31
|
+
run_workflow=lambda wf_id, inputs: platform.run_workflow_by_id(
|
|
32
|
+
wf_id,
|
|
33
|
+
None,
|
|
34
|
+
None,
|
|
35
|
+
with_agent=False,
|
|
36
|
+
inputs=inputs,
|
|
37
|
+
),
|
|
38
|
+
data_dir=data_dir,
|
|
39
|
+
review_sink=review_queue,
|
|
40
|
+
)
|
|
41
|
+
agent_runtime = AgentRuntime(
|
|
42
|
+
store=store,
|
|
43
|
+
orchestrator_factory=platform.build_orchestrator,
|
|
44
|
+
workspace_graph=workspace_graph,
|
|
45
|
+
append_audit_event=append_audit_event,
|
|
46
|
+
hooks=hooks,
|
|
47
|
+
)
|
|
48
|
+
run_executor = RunExecutor(
|
|
49
|
+
store=store,
|
|
50
|
+
agent_runtime=agent_runtime,
|
|
51
|
+
build_workflow_runners=platform.build_workflow_runners,
|
|
52
|
+
workspace_graph=workspace_graph,
|
|
53
|
+
append_audit_event=append_audit_event,
|
|
54
|
+
hooks=hooks,
|
|
55
|
+
review_sink=review_queue,
|
|
56
|
+
)
|
|
57
|
+
agent_runtime.attach_executor(run_executor)
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
"REVIEW_QUEUE": review_queue,
|
|
61
|
+
"TRIGGER_SERVICE": trigger_service,
|
|
62
|
+
"AGENT_RUNTIME": agent_runtime,
|
|
63
|
+
"RUN_EXECUTOR": run_executor,
|
|
64
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Session bootstrap runtime: session store and token helpers.
|
|
2
|
+
|
|
3
|
+
Extracted from ``app_factory._build`` as a composition seam. The session
|
|
4
|
+
token helpers stay closures over a single ``SessionStore`` so the factory
|
|
5
|
+
keeps one source of truth for token lifecycle. ``user_id_resolver`` is the
|
|
6
|
+
factory's ``user_id_for_email`` — injected so the store stays decoupled from
|
|
7
|
+
user persistence. Heavy imports stay inside the function so importing the
|
|
8
|
+
module has no side effects.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import Any, Callable, Dict, Optional
|
|
14
|
+
|
|
15
|
+
_DEFAULT_SESSION_TTL = 60 * 60 * 24
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def build_session_runtime(
|
|
19
|
+
*,
|
|
20
|
+
user_id_resolver: Callable[[Optional[str]], Optional[str]],
|
|
21
|
+
ttl_seconds: int = _DEFAULT_SESSION_TTL,
|
|
22
|
+
) -> Dict[str, Any]:
|
|
23
|
+
"""Construct the session store and its token helper closures."""
|
|
24
|
+
|
|
25
|
+
from latticeai.core.sessions import SessionStore
|
|
26
|
+
|
|
27
|
+
session_store = SessionStore()
|
|
28
|
+
|
|
29
|
+
def create_session(email: str) -> str:
|
|
30
|
+
return session_store.create(user_id_resolver(email) or email, email=email)
|
|
31
|
+
|
|
32
|
+
def get_session_email(token: str) -> Optional[str]:
|
|
33
|
+
return session_store.get_email(token)
|
|
34
|
+
|
|
35
|
+
def get_session_user_id(token: str) -> Optional[str]:
|
|
36
|
+
return session_store.get_subject(token)
|
|
37
|
+
|
|
38
|
+
def invalidate_session(token: str) -> None:
|
|
39
|
+
session_store.invalidate(token)
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
"_SESSION_TTL": ttl_seconds,
|
|
43
|
+
"_session_store": session_store,
|
|
44
|
+
"create_session": create_session,
|
|
45
|
+
"get_session_email": get_session_email,
|
|
46
|
+
"get_session_user_id": get_session_user_id,
|
|
47
|
+
"invalidate_session": invalidate_session,
|
|
48
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Retrieval/context runtime assembly for app startup."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Callable, Dict
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def build_context_runtime(
|
|
9
|
+
*,
|
|
10
|
+
graph_store: Any,
|
|
11
|
+
ingestion_pipeline: Any,
|
|
12
|
+
memory_service: Any,
|
|
13
|
+
gardener: Any,
|
|
14
|
+
require_auth: bool,
|
|
15
|
+
allowed_scopes_for_user: Callable[[Any], Any],
|
|
16
|
+
) -> Dict[str, Any]:
|
|
17
|
+
"""Construct search, brain memory, and context assembly services."""
|
|
18
|
+
|
|
19
|
+
from lattice_brain.context import ContextAssembler
|
|
20
|
+
from lattice_brain.memory import BrainMemory
|
|
21
|
+
from latticeai.services.search_service import SearchService
|
|
22
|
+
|
|
23
|
+
search_service = SearchService(graph_store=graph_store)
|
|
24
|
+
brain_memory = BrainMemory(ingestion_pipeline)
|
|
25
|
+
|
|
26
|
+
def scoped_hybrid_search(q, user_email=None, **kw):
|
|
27
|
+
allowed = None
|
|
28
|
+
if require_auth and user_email:
|
|
29
|
+
allowed = allowed_scopes_for_user(user_email)
|
|
30
|
+
return search_service.hybrid_search(q, allowed_workspaces=allowed, **kw)
|
|
31
|
+
|
|
32
|
+
context_assembler = ContextAssembler(
|
|
33
|
+
memory_recall=memory_service.recall,
|
|
34
|
+
hybrid_search=scoped_hybrid_search,
|
|
35
|
+
notes_context=gardener.get_relevant_context,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
"SEARCH_SERVICE": search_service,
|
|
40
|
+
"BRAIN_MEMORY": brain_memory,
|
|
41
|
+
"CONTEXT_ASSEMBLER": context_assembler,
|
|
42
|
+
"_scoped_hybrid_search": scoped_hybrid_search,
|
|
43
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Hooks + local-knowledge watcher runtime assembly for app startup.
|
|
2
|
+
|
|
3
|
+
Extracted from ``app_factory._build`` as a composition seam: the hooks
|
|
4
|
+
registry must be constructed *ahead* of the local-knowledge watcher so
|
|
5
|
+
folder-watch reindexes can fire the ``pre_index``/``post_index`` lifecycle
|
|
6
|
+
hooks. Heavy imports stay inside the function so importing the module has no
|
|
7
|
+
side effects.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import Any, Callable, Dict
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def build_hooks_runtime(
|
|
16
|
+
*,
|
|
17
|
+
data_dir: Any,
|
|
18
|
+
enable_graph: bool,
|
|
19
|
+
knowledge_graph_getter: Callable[[], Any],
|
|
20
|
+
) -> Dict[str, Any]:
|
|
21
|
+
"""Construct the hooks registry and local-knowledge watcher behind one seam."""
|
|
22
|
+
|
|
23
|
+
from lattice_brain.runtime.hooks import HooksRegistry
|
|
24
|
+
from local_knowledge_api import LocalKnowledgeWatcher
|
|
25
|
+
|
|
26
|
+
hooks_registry = HooksRegistry(data_dir / "hooks.json")
|
|
27
|
+
local_kg_watcher = (
|
|
28
|
+
LocalKnowledgeWatcher(knowledge_graph_getter, hooks=hooks_registry)
|
|
29
|
+
if enable_graph
|
|
30
|
+
else None
|
|
31
|
+
)
|
|
32
|
+
return {
|
|
33
|
+
"HOOKS_REGISTRY": hooks_registry,
|
|
34
|
+
"LOCAL_KG_WATCHER": local_kg_watcher,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def bind_trigger_hook_runner(*, registry: Any, trigger_service: Any) -> str:
|
|
39
|
+
"""Ensure the brain-event trigger hook exists and bind its runtime runner."""
|
|
40
|
+
|
|
41
|
+
from latticeai.services.triggers import TRIGGER_HOOK_NAME
|
|
42
|
+
|
|
43
|
+
trigger_hook_id = next(
|
|
44
|
+
(
|
|
45
|
+
h.get("id")
|
|
46
|
+
for h in registry._state.get("custom", [])
|
|
47
|
+
if h.get("name") == TRIGGER_HOOK_NAME
|
|
48
|
+
),
|
|
49
|
+
None,
|
|
50
|
+
)
|
|
51
|
+
if trigger_hook_id is None:
|
|
52
|
+
trigger_hook_id = registry.register(
|
|
53
|
+
name=TRIGGER_HOOK_NAME,
|
|
54
|
+
kind="post_tool",
|
|
55
|
+
description="Fires brain_event workflow triggers when knowledge enters the brain.",
|
|
56
|
+
)["id"]
|
|
57
|
+
registry.register_hook(trigger_hook_id, trigger_service.hook_runner())
|
|
58
|
+
return trigger_hook_id
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def bind_builtin_hook_runners(
|
|
62
|
+
*,
|
|
63
|
+
registry: Any,
|
|
64
|
+
append_audit_event: Callable[..., Any],
|
|
65
|
+
get_tool_permission: Callable[..., Any],
|
|
66
|
+
classify_sensitive_message: Callable[..., Any],
|
|
67
|
+
) -> None:
|
|
68
|
+
"""Bind concrete platform runners for built-in hook definitions."""
|
|
69
|
+
|
|
70
|
+
from latticeai.core.builtin_hooks import register_builtin_hook_runners
|
|
71
|
+
|
|
72
|
+
register_builtin_hook_runners(
|
|
73
|
+
registry,
|
|
74
|
+
append_audit_event=append_audit_event,
|
|
75
|
+
get_tool_permission=get_tool_permission,
|
|
76
|
+
classify_sensitive_message=classify_sensitive_message,
|
|
77
|
+
)
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""FastAPI lifespan assembly for app startup and shutdown tasks."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from contextlib import asynccontextmanager
|
|
6
|
+
from typing import Any, Dict
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def build_lifespan_runtime(
|
|
10
|
+
*,
|
|
11
|
+
app_mode: str,
|
|
12
|
+
enable_telegram: bool,
|
|
13
|
+
autoload_models: bool,
|
|
14
|
+
is_public_mode: bool,
|
|
15
|
+
public_model: str,
|
|
16
|
+
allow_local_models: bool,
|
|
17
|
+
local_model: str,
|
|
18
|
+
local_draft_model: str,
|
|
19
|
+
model_idle_unload_seconds: int,
|
|
20
|
+
model_router: Any,
|
|
21
|
+
local_kg_watcher: Any,
|
|
22
|
+
local_server_processes: Dict[str, Any],
|
|
23
|
+
logger: Any,
|
|
24
|
+
) -> Dict[str, Any]:
|
|
25
|
+
"""Create lifespan and background task helpers for the FastAPI app."""
|
|
26
|
+
|
|
27
|
+
import asyncio
|
|
28
|
+
import os
|
|
29
|
+
|
|
30
|
+
async def autoload_default_model() -> None:
|
|
31
|
+
if not autoload_models:
|
|
32
|
+
print("⏭️ Model autoload disabled by LATTICEAI_AUTOLOAD_MODELS=false.")
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
if is_public_mode:
|
|
36
|
+
model_id = public_model
|
|
37
|
+
provider = model_id.split(":", 1)[0] if ":" in model_id else "openai"
|
|
38
|
+
env_by_provider = {
|
|
39
|
+
"openai": "OPENAI_API_KEY",
|
|
40
|
+
"openrouter": "OPENROUTER_API_KEY",
|
|
41
|
+
"groq": "GROQ_API_KEY",
|
|
42
|
+
"together": "TOGETHER_API_KEY",
|
|
43
|
+
"ollama": "OLLAMA_API_KEY",
|
|
44
|
+
}
|
|
45
|
+
required_env = env_by_provider.get(provider)
|
|
46
|
+
if required_env and not os.getenv(required_env) and provider != "ollama":
|
|
47
|
+
print(f"🌐 Public mode ready. Set {required_env} to autoload {model_id}.")
|
|
48
|
+
return
|
|
49
|
+
print(f"🌐 Public mode autoload: {model_id}")
|
|
50
|
+
try:
|
|
51
|
+
msg = await model_router.load_model(model_id)
|
|
52
|
+
print(f"✅ {msg}")
|
|
53
|
+
except Exception as e: # pragma: no cover - startup diagnostics
|
|
54
|
+
print(f"⚠️ Public model autoload failed: {e}")
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
if not allow_local_models:
|
|
58
|
+
print("⏭️ Local model autoload skipped because LATTICEAI_ALLOW_LOCAL_MODELS=false.")
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
print("⏳ Auto-loading local model stack:")
|
|
62
|
+
print(f" - Target: {local_model}")
|
|
63
|
+
if local_draft_model:
|
|
64
|
+
print(f" - Draft: {local_draft_model}")
|
|
65
|
+
else:
|
|
66
|
+
print(" - Draft: disabled (set LATTICEAI_LOCAL_DRAFT_MODEL to enable)")
|
|
67
|
+
try:
|
|
68
|
+
await model_router.load_model(local_model, draft_model_id=local_draft_model or None)
|
|
69
|
+
except Exception as e: # pragma: no cover - startup diagnostics
|
|
70
|
+
print(f"⚠️ Local model autoload failed: {e}")
|
|
71
|
+
|
|
72
|
+
async def unload_idle_models_loop() -> None:
|
|
73
|
+
if model_idle_unload_seconds <= 0:
|
|
74
|
+
print("⏭️ Model idle unload disabled.")
|
|
75
|
+
return
|
|
76
|
+
while True:
|
|
77
|
+
await asyncio.sleep(min(60, model_idle_unload_seconds))
|
|
78
|
+
try:
|
|
79
|
+
unloaded = model_router.unload_idle_models(model_idle_unload_seconds)
|
|
80
|
+
if unloaded:
|
|
81
|
+
print(f"🧹 Idle model unload: {', '.join(unloaded)}")
|
|
82
|
+
except Exception as e: # pragma: no cover - background diagnostics
|
|
83
|
+
logger.warning("Idle model unload failed: %s", e)
|
|
84
|
+
|
|
85
|
+
def spawn(coro, *, name: str):
|
|
86
|
+
"""Fire-and-forget asyncio task that logs exceptions instead of swallowing them."""
|
|
87
|
+
|
|
88
|
+
task = asyncio.create_task(coro, name=name)
|
|
89
|
+
|
|
90
|
+
def _on_done(t: asyncio.Task) -> None:
|
|
91
|
+
if t.cancelled():
|
|
92
|
+
return
|
|
93
|
+
exc = t.exception()
|
|
94
|
+
if exc is not None:
|
|
95
|
+
logger.warning("background task '%s' failed: %s", name, exc)
|
|
96
|
+
|
|
97
|
+
task.add_done_callback(_on_done)
|
|
98
|
+
return task
|
|
99
|
+
|
|
100
|
+
@asynccontextmanager
|
|
101
|
+
async def lifespan(_app):
|
|
102
|
+
try:
|
|
103
|
+
print(f"🧭 Lattice AI mode: {app_mode}")
|
|
104
|
+
if enable_telegram:
|
|
105
|
+
from telegram_bot import run_bot
|
|
106
|
+
|
|
107
|
+
spawn(run_bot(), name="telegram_bot")
|
|
108
|
+
print("🚀 Telegram Bot Bridge activated!")
|
|
109
|
+
else:
|
|
110
|
+
print("⏭️ Telegram Bot Bridge disabled for this mode.")
|
|
111
|
+
spawn(unload_idle_models_loop(), name="unload_idle_models")
|
|
112
|
+
spawn(autoload_default_model(), name="autoload_default_model")
|
|
113
|
+
if local_kg_watcher:
|
|
114
|
+
restored = local_kg_watcher.restore_enabled_sources()
|
|
115
|
+
if restored.get("restored"):
|
|
116
|
+
print(f"🕸️ Local knowledge watchers restored: {restored['restored']}")
|
|
117
|
+
except Exception as e: # pragma: no cover - startup diagnostics
|
|
118
|
+
print(f"⚠️ Startup sequence failed: {e}")
|
|
119
|
+
try:
|
|
120
|
+
yield
|
|
121
|
+
finally:
|
|
122
|
+
if local_kg_watcher:
|
|
123
|
+
local_kg_watcher.stop_all()
|
|
124
|
+
model_router.unload_all()
|
|
125
|
+
for proc in local_server_processes.values():
|
|
126
|
+
try:
|
|
127
|
+
if proc.poll() is None:
|
|
128
|
+
proc.terminate()
|
|
129
|
+
proc.wait(timeout=5)
|
|
130
|
+
except Exception:
|
|
131
|
+
pass
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
"autoload_default_model": autoload_default_model,
|
|
135
|
+
"unload_idle_models_loop": unload_idle_models_loop,
|
|
136
|
+
"_spawn": spawn,
|
|
137
|
+
"lifespan": lifespan,
|
|
138
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Persistence/service assembly seams for app startup.
|
|
2
|
+
|
|
3
|
+
This module owns the durable local stores and services that sit between the
|
|
4
|
+
Brain runtime and API routers. Imports stay inside the function so importing
|
|
5
|
+
``latticeai.app_factory`` remains side-effect free.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any, Callable, Dict, Optional
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def build_persistence_runtime(
|
|
14
|
+
*,
|
|
15
|
+
data_dir: Any,
|
|
16
|
+
base_dir: Any,
|
|
17
|
+
enable_graph: bool,
|
|
18
|
+
knowledge_graph: Any,
|
|
19
|
+
hooks_registry: Any,
|
|
20
|
+
history_file: Any,
|
|
21
|
+
conversations: Any,
|
|
22
|
+
user_id_for_email: Callable[[Optional[str]], Optional[str]],
|
|
23
|
+
audit: Callable[[str, Dict[str, Any], Optional[str]], None],
|
|
24
|
+
) -> Dict[str, Any]:
|
|
25
|
+
"""Construct workspace, plugin, memory, ingestion, and portability services."""
|
|
26
|
+
|
|
27
|
+
import os
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
from lattice_brain.identity import DeviceIdentity
|
|
31
|
+
from lattice_brain.ingestion import IngestionPipeline
|
|
32
|
+
from lattice_brain.portability import KGPortabilityService
|
|
33
|
+
from latticeai.core.agent_registry import AgentRegistry
|
|
34
|
+
from latticeai.core.invitations import InvitationStore
|
|
35
|
+
from latticeai.core.marketplace import TemplateCatalog
|
|
36
|
+
from latticeai.core.plugins import PluginRegistry
|
|
37
|
+
from latticeai.core.realtime import RealtimeBus
|
|
38
|
+
from latticeai.core.workspace_os import WorkspaceOSStore
|
|
39
|
+
from latticeai.services.memory_service import MemoryService
|
|
40
|
+
from latticeai.services.workspace_service import WorkspaceService
|
|
41
|
+
|
|
42
|
+
realtime_bus = RealtimeBus()
|
|
43
|
+
workspace_os = WorkspaceOSStore(data_dir, event_sink=realtime_bus)
|
|
44
|
+
workspace_service = WorkspaceService(workspace_os, resolve_user_id=user_id_for_email)
|
|
45
|
+
invitation_store = InvitationStore(data_dir / "invitations.json")
|
|
46
|
+
|
|
47
|
+
plugins_dir = Path(os.getenv("LATTICEAI_PLUGINS_DIR") or (base_dir / "plugins"))
|
|
48
|
+
plugin_registry = PluginRegistry(plugins_dir, store=workspace_os)
|
|
49
|
+
template_catalog = TemplateCatalog()
|
|
50
|
+
agent_registry = AgentRegistry(data_dir / "agent_registry.json")
|
|
51
|
+
|
|
52
|
+
memory_service = MemoryService(
|
|
53
|
+
store=workspace_os,
|
|
54
|
+
data_dir=data_dir,
|
|
55
|
+
knowledge_graph=knowledge_graph,
|
|
56
|
+
enable_graph=enable_graph,
|
|
57
|
+
history_file=history_file,
|
|
58
|
+
conversation_store=conversations,
|
|
59
|
+
)
|
|
60
|
+
ingestion_pipeline = IngestionPipeline(
|
|
61
|
+
knowledge_graph,
|
|
62
|
+
hooks=hooks_registry,
|
|
63
|
+
enable_graph=enable_graph,
|
|
64
|
+
audit=audit,
|
|
65
|
+
)
|
|
66
|
+
device_identity = DeviceIdentity(data_dir)
|
|
67
|
+
kg_portability = KGPortabilityService(
|
|
68
|
+
knowledge_graph=knowledge_graph,
|
|
69
|
+
data_dir=data_dir,
|
|
70
|
+
enable_graph=enable_graph,
|
|
71
|
+
device_identity=device_identity,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
"REALTIME_BUS": realtime_bus,
|
|
76
|
+
"WORKSPACE_OS": workspace_os,
|
|
77
|
+
"WORKSPACE_SERVICE": workspace_service,
|
|
78
|
+
"INVITATION_STORE": invitation_store,
|
|
79
|
+
"PLUGINS_DIR": plugins_dir,
|
|
80
|
+
"PLUGIN_REGISTRY": plugin_registry,
|
|
81
|
+
"TEMPLATE_CATALOG": template_catalog,
|
|
82
|
+
"AGENT_REGISTRY": agent_registry,
|
|
83
|
+
"MEMORY_SERVICE": memory_service,
|
|
84
|
+
"INGESTION_PIPELINE": ingestion_pipeline,
|
|
85
|
+
"DEVICE_IDENTITY": device_identity,
|
|
86
|
+
"KG_PORTABILITY": kg_portability,
|
|
87
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Small platform service construction seams for app startup."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def build_model_service(
|
|
9
|
+
*,
|
|
10
|
+
model_router: Any,
|
|
11
|
+
runtime_features: Any,
|
|
12
|
+
is_public: bool,
|
|
13
|
+
) -> Any:
|
|
14
|
+
"""Construct the health/model summary service."""
|
|
15
|
+
|
|
16
|
+
from latticeai.services.model_service import ModelService
|
|
17
|
+
|
|
18
|
+
return ModelService(
|
|
19
|
+
model_router=model_router,
|
|
20
|
+
runtime_features=runtime_features,
|
|
21
|
+
is_public=is_public,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def build_brain_network(
|
|
26
|
+
*,
|
|
27
|
+
identity: Any,
|
|
28
|
+
portability: Any,
|
|
29
|
+
data_dir: Any,
|
|
30
|
+
) -> Any:
|
|
31
|
+
"""Construct peer sync/network service for brain portability routes."""
|
|
32
|
+
|
|
33
|
+
from lattice_brain.network import BrainNetwork
|
|
34
|
+
|
|
35
|
+
return BrainNetwork(
|
|
36
|
+
identity=identity,
|
|
37
|
+
portability=portability,
|
|
38
|
+
data_dir=data_dir,
|
|
39
|
+
)
|