Application Bootstrap and Embedding Guide
Application Bootstrap and Embedding Guide
This document explains when and in what order LevPRO AI runtime components are initialized, and how to embed the runtime into another application. For class-level API details, see docs/api/interfaces.md.
Overview: Two Phases
| Phase | When | What happens |
|---|---|---|
| Construction | Sync, before async work | create_app() wires objects; planner/autotune; dirs created; mcp.json merged |
| Runtime startup | Async, before agent runs | ctx.start_supervisors() then ctx.startup() |
| Operation | Async | session_runner.run(...) or agent_loop.run(...) |
| Shutdown | Async, always in finally |
ctx.shutdown() |
Do not call agent_loop.run() until llama-server is healthy. Do not skip ctx.shutdown() — it stops the memory index watcher (when enabled), terminates llama-server process(es), closes MCP sessions, and closes the HTTP client pool.
Chat-only vs project mode
| Mode | Config | Tools |
|---|---|---|
| Default | Omit workspace (or workspace: null) |
Native recovery tools + mcp_* from mcp.json |
| Project | workspace: "/path/to/project" |
Same tools; optional root for skills storage (not a Core FS index/sandbox) |
Capability tools (filesystem, shell, git, HTTP, …) always come from MCP — not from built-in Core packages. Runtime state (sessions, L0–L3, RAG, mcp.json) uses runtime_data_dir (default ~/.local-ai/).
Quick Start: Full Embed (Recommended)
Canonical pattern from packages/local-ai-core/interfaces/cli.py:
import asyncio
from agents.registry import get_agent_config
from app import create_app, default_agent_name
from config.loader import load_config
from core.shutdown import install_signal_handlers
async def main() -> None:
config = load_config("config.yaml")
ctx = create_app(config=config)
install_signal_handlers(ctx) # optional
await ctx.start_supervisors()
await ctx.startup()
try:
agent_name = default_agent_name(ctx.config)
agent_config = get_agent_config(ctx.config, agent_name)
result = await ctx.session_runner.run(
user_id="user1",
session_id="session_123",
user_input="Hello",
agent_config=agent_config,
agent_name=agent_name,
)
print(result.response)
finally:
await ctx.shutdown()
asyncio.run(main())
Prerequisites:
- Valid
config.yamlwithllama.binary_path,llama.models.main.path, and at least one agent - MCP servers in
{runtime_data_dir}/mcp.json(or--mcp-config/LOCAL_AI_MCP_CONFIG) when the agent needs tools - For RAG/L0–L3:
pip install -e "packages/local-ai-core[memory]"andmemory.enabled: true create_app()raisesModelPlanErrorif the model cannot fit in available RAM/VRAM
Optional extras (install + enable in config):
| Extra | Install | Config gate |
|---|---|---|
[memory] |
pip install -e "packages/local-ai-core[memory]" |
memory.enabled: true |
[ensemble] |
pip install -e "packages/local-ai-core[ensemble]" |
ensemble.enabled: true |
[skills] |
pip install -e "packages/local-ai-core[skills]" |
skills.enabled: true |
[monitor] |
pip install -e "packages/local-ai-core[monitor]" |
monitor.enabled: true |
[autotune] |
pip install -e "packages/local-ai-core[autotune]" |
llama.autotune.enabled: true |
| all | pip install -e "packages/local-ai-core[all]" |
enable each section as needed |
local-ai-mcp is a hard dependency of local-ai-core (not an optional extra). Enable via mcp.json / --mcp-config / LOCAL_AI_MCP_CONFIG.
Host injection of MCP: host_integrations.md.
Construction: create_app() Dependency Order
packages/local-ai-core/app.py remains the public composition root and wires dependencies synchronously in this order. Focused construction logic lives under core/bootstrap/: inference creation, model planning, resource/degradation wiring, and tool-registration validation. Runtime startup/shutdown ownership lives in core/app_context.py.
load_config()(or use passedconfig)apply_env_overrides()(LOCAL_AI_MCP_CONFIG)setup_logging()merge_mcp_external()— load{runtime_data_dir}/mcp.json(ormcp.external_path) as sole server sourceDirectoryGuard(runtime_root)— mkdirsessions/; optional workspaceDirectoryGuard- Optional ensemble (when
local-ai-ensembleinstalled andensemble.enabled: true):
build_ensemble_stack()→EnsembleRouter+ oneProcessSupervisorperllama.modelsentryllm_clientbecomes the router;supervisorslist holds all model supervisors
- Single model (default): planner and/or autotune →
ResolvedLlamaModelConfig
ProcessSupervisor(binary_path, resolved model, …)→base_urlLocalLLMClient(base_url, timeouts, model_name)
TokenCounter(llm_client)→ContextBuilderSessionStore(guard, max_session_size)- Optional memory (when
local-ai-memoryinstalled andmemory.enabled: true):
build_memory_stack()→MemoryStack(store, retriever, manager, compressor, index_service, tools)- mkdir
memory_data/knowledge/ memory_retriever= stack retriever; elseNullMemoryRetriever
- MCP (when
mcp.enabledand servers from mcp.json):
build_mcp_stack()→ lazymcp_*tools (stdio and/or HTTP)
- Optional skills (
skills.enabled) →SkillsStack(retriever + capture); elseNullSkillsRetriever - Message archive tools when
message_archive_enabled build_tool_executor(config, …, extra_tools=..., auto_enable=mcp names)PromptManager(base_dir=config directory)AgentLoop(...)with retriever protocols (mcp_*+ recovery tools auto-granted)SessionRunner(agent_loop, max_concurrent_sessions)- Optional monitor stack when
monitor.enabled AppContext(...)
When an optional package is installed but its config flag is false, a warning is logged at startup.
Rules:
- Do not construct
ProcessSupervisorbefore plan/autotune resolution (single-model path). - Do not call
memory_stack.index_service.startup()insidecreate_app()— useawait ctx.startup()(also discovers MCP tools whenallowed_tools: ["*"]). - Prefer
create_app()over manual wiring unless injecting mocks. - Hosts may pass CLI overrides (
--mcp-config) beforecreate_app(config=...); env overrides are applied again insidecreate_app.
Runtime Lifecycle Checklist
| Step | Required for agent? | Skip when |
|---|---|---|
create_app() |
Yes | Never for full agent |
install_signal_handlers(ctx) |
Optional | Non-interactive hosts |
await ctx.start_supervisors() |
Yes (default) | External llama-server already healthy |
await ctx.startup() |
Yes when memory.enabled or MCP discover-all |
Memory disabled and no discover-all |
await ctx.session_runner.run(...) |
Yes (preferred) | Low-level tests may use agent_loop.run |
await ctx.shutdown() |
Always in finally |
— |
start_supervisors()
Starts every supervisor in ctx.supervisors (ensemble mode) or ctx.supervisor (single-model). Use this instead of ctx.supervisor.start() directly so ensemble multi-model setups start all llama-server processes.
AppContext public surface
| Field / method | Purpose |
|---|---|
config |
Typed configuration |
supervisor |
Primary llama-server watchdog (first in ensemble list) |
supervisors |
All supervisors when [ensemble] enabled; else [supervisor] |
llm_client |
LocalLLMClient or EnsembleRouter when ensemble enabled |
agent_loop |
ReAct orchestrator — low-level API |
session_runner |
Concurrent sessions + memory scope — preferred embed path |
runtime_guard / guard |
DirectoryGuard over runtime_data_dir (sessions, memory, archives) |
workspace_guard |
Optional DirectoryGuard over workspace (skills storage root when set); else None |
memory_stack |
MemoryStack when memory enabled; else None |
mcp_stack |
MCP bridge when servers configured; else None |
skills_stack |
SkillsStack when skills enabled; else None |
ensemble_stack |
EnsembleStack when ensemble enabled; else None |
monitor_stack |
MonitorStack when [monitor] enabled; else None |
degradation |
GracefulDegradation when degradation enabled; else None |
autotune_stack |
Autotune stack when [autotune] enabled; else None |
background_tasks |
Registry for background tasks (e.g. autotune) |
shutdown_event |
App shutdown (SIGINT); AgentLoop saves interrupted L2 checkpoint |
Per-run cancel_event |
stdio cancel method; cancels current chat without shutting down the server |
async start_supervisors() |
Start all llama-server processes |
async startup() |
Memory index startup + MCP discover-all when needed |
async shutdown() |
Stop index watcher → MCP cleanup → stop supervisors → close LLM client |
What ctx.startup() does
- When
memory_stackis wired: builds/refreshes the memory index and starts the watcher - When MCP servers request discover-all (
allowed_tools: ["*"]or empty): connects and registers tools
Electron / stdio embedding
local-ai serve-stdio --config config.yaml --mcp-config path/to/mcp.json
Protocol: docs/stdio_protocol.md.
For in-process Python embedding, use SessionRunner as above.
Selective Component Embedding
| Goal | Start from | Notes |
|---|---|---|
| Run agent (default) | create_app() |
Full lifecycle |
| Chat only, no memory index | create_app() |
Skip ctx.startup() when memory.enabled: false and no MCP discover-all |
| RAG retrieval only | build_memory_stack() pieces |
Needs running llama-server |
| Multi-model routing | build_ensemble_stack() |
Requires [ensemble] + multiple llama.models |
| Planner preview | llama-tools plan |
No create_app |
| System diagnostics | llama-tools doctor |
No create_app |
| Benchmark | llama-tools benchmark |
No create_app |
External llama-server
- Ensure health on
config.llama.models.main.port(orconfig.llama.main.portproperty) - Skip
await ctx.start_supervisors()if already healthy TokenCounterstill requires/tokenizeon the running server
Failure Modes and Pitfalls
| Problem | Cause | Fix |
|---|---|---|
ModelPlanError at startup |
Model cannot fit RAM/VRAM | Fix model, hardware, or planner overrides |
| LLM timeouts on first run | agent_loop.run() before start_supervisors() |
Call await ctx.start_supervisors() first |
| Stale or missing RAG | Skipped ctx.startup() with memory.enabled |
Call await ctx.startup() |
| Memory features inactive | Package installed but memory.enabled: false |
Set memory.enabled: true in config |
| No capability tools | Empty/missing mcp.json |
Add servers; pass --mcp-config or set mcp.external_path |
YAML mcp.servers ignored |
Sole source is mcp.json | Put servers in the JSON file |
| Orphan llama-server | Skipped ctx.shutdown() |
Always use try / finally |
Related Documentation
docs/architecture.md— layer responsibilitiesdocs/api/interfaces.md—AppContextAPI referencedocs/extension_points.md— adding tools and interfacesdocs/mcp.md— MCP bridgeAGENTS.md— AI coding assistant guide