pub struct AppState {Show 22 fields
pub app_data_dir: PathBuf,
pub config: Arc<RwLock<Config>>,
pub provider: Arc<RwLock<Arc<dyn LLMProvider>>>,
pub sessions: Arc<RwLock<HashMap<String, Session>>>,
pub storage: Arc<dyn Storage>,
pub session_store: Arc<SessionStoreV2>,
pub spawn_scheduler: Arc<SpawnScheduler>,
pub schedule_store: Arc<ScheduleStore>,
pub schedule_manager: Arc<ScheduleManager>,
pub tool_factory: ToolSurfaceFactory,
pub cancel_tokens: Arc<RwLock<HashMap<String, CancellationToken>>>,
pub skill_manager: Arc<SkillManager>,
pub mcp_manager: Arc<McpServerManager>,
pub metrics_service: Arc<MetricsService>,
pub agent_runners: Arc<RwLock<HashMap<String, AgentRunner>>>,
pub session_event_senders: Arc<RwLock<HashMap<String, Sender<AgentEvent>>>>,
pub process_registry: Arc<ProcessRegistry>,
pub metrics_bus: Option<MetricsBus>,
pub agent: Arc<Agent>,
pub provider_registry: Arc<ProviderRegistry>,
pub provider_router: Arc<ProviderModelRouter>,
pub model_catalog: Arc<ModelCatalogService>,
/* private fields */
}Expand description
Unified application state consolidating web_service and agent/server state
This struct holds all the state needed to run the Bamboo server, including configuration, LLM providers, sessions, storage, tools, skills, and metrics.
§Design Goals
- Direct access: Components are directly accessible without HTTP proxies
- Hot reload: Configuration and providers can be reloaded at runtime
- Thread safety: Uses Arc
for concurrent access - Persistence: Integrates with JsonlStorage for session persistence
§Component Overview
| Component | Purpose | Thread-Safe |
|---|---|---|
config | Application configuration | Yes (RwLock) |
provider | Hot-reloadable LLM provider | Yes (RwLock) |
sessions | Active conversation sessions | Yes (RwLock) |
storage | Persistent session storage | Yes (Arc) |
tools | Tool execution (builtin + MCP) | Yes (Arc) |
skill_manager | Skill registry and execution | Yes (Arc) |
mcp_manager | MCP server lifecycle | Yes (Arc) |
metrics_service | Usage metrics collection | Yes (Arc) |
agent_runners | Active agent executions | Yes (RwLock) |
Fields§
§app_data_dir: PathBufApplication data directory (configured via BAMBOO_DATA_DIR; default ${HOME}/.bamboo)
config: Arc<RwLock<Config>>Hot-reloadable application configuration
Can be reloaded from disk at runtime using reload_config().
provider: Arc<RwLock<Arc<dyn LLMProvider>>>Hot-reloadable LLM provider with direct access
This eliminates the proxy pattern where we created an AgentAppState that called back to web_service via HTTP. Now we have direct provider access.
sessions: Arc<RwLock<HashMap<String, Session>>>Active conversation sessions (in-memory cache)
Maps session IDs to Session objects. Persisted to storage
via the storage field.
storage: Arc<dyn Storage>Persistent storage backend for sessions (V2).
Implemented as folder-per-session with a global sessions.json index.
session_store: Arc<SessionStoreV2>Concrete session store implementation (for index/list/cleanup APIs).
spawn_scheduler: Arc<SpawnScheduler>Background scheduler for async sub-session spawning.
schedule_store: Arc<ScheduleStore>Schedule store (timed tasks).
schedule_manager: Arc<ScheduleManager>Background schedule manager that triggers scheduled runs.
tool_factory: ToolSurfaceFactoryTool surface factory providing pre-built tool executors for each session type.
Use state.tools_for(ToolSurface::Root) for root sessions,
state.tools_for(ToolSurface::Child) for child sessions, etc.
cancel_tokens: Arc<RwLock<HashMap<String, CancellationToken>>>Cancellation tokens for in-flight requests
Maps request/session IDs to their cancellation tokens, allowing graceful shutdown of long-running operations.
skill_manager: Arc<SkillManager>Skill manager for prompt-based skill execution
Manages the skill registry and handles skill lookup, validation, and execution.
mcp_manager: Arc<McpServerManager>MCP server manager for external tool servers
Handles lifecycle of Model Context Protocol servers, including initialization, tool discovery, and shutdown.
metrics_service: Arc<MetricsService>Metrics collection and persistence service
Tracks token usage, costs, and performance metrics across all sessions.
agent_runners: Arc<RwLock<HashMap<String, AgentRunner>>>Active agent runners indexed by session ID
Each runner manages event broadcasting and cancellation for an active agent execution.
session_event_senders: Arc<RwLock<HashMap<String, Sender<AgentEvent>>>>Session-scoped event streams (long-lived).
Unlike agent_runners, these senders exist even when no agent execution is running.
They are used for:
- UI subscriptions to
/api/v1/events/{session_id}(background tasks, etc.) - sub-session forwarding (child -> parent)
process_registry: Arc<ProcessRegistry>Registry for tracking external processes.
metrics_bus: Option<MetricsBus>Optional metrics bus for event streaming
When enabled, allows subscribing to metrics events in real-time.
agent: Arc<Agent>Unified agent execution runtime holding shared resources.
provider_registry: Arc<ProviderRegistry>Multi-provider registry (used when features.provider_model_ref is enabled).
provider_router: Arc<ProviderModelRouter>Provider/model router (used when features.provider_model_ref is enabled).
model_catalog: Arc<ModelCatalogService>Unified model catalog service (used when features.provider_model_ref is enabled).
Implementations§
Source§impl AppState
impl AppState
Sourcepub async fn new(bamboo_home_dir: PathBuf) -> Result<Self, AppError>
pub async fn new(bamboo_home_dir: PathBuf) -> Result<Self, AppError>
Create unified app state with direct provider access
This eliminates the proxy pattern where we created an AgentAppState that called back to web_service via HTTP. Now we have direct provider access.
§Arguments
bamboo_home_dir- Bamboo home directory containing all application data. This is the root directory (e.g.,${HOME}/.bamboo) that contains:- config.json: Configuration file
- sessions/: Conversation history
- skills/: Skill definitions
- workflows/: Workflow definitions
- cache/: Cached data
- runtime/: Runtime files
§Returns
A fully initialized AppState with all components ready for use.
§Example
use bamboo_server::app_state::AppState;
use std::path::PathBuf;
#[tokio::main]
async fn main() {
let state = AppState::new(PathBuf::from("/path/to/bamboo-data-dir"))
.await
.expect("failed to initialize app state");
let provider = state.get_provider().await;
let _models = provider.list_models().await.ok();
}Sourcepub async fn new_with_provider(
bamboo_home_dir: PathBuf,
config: Config,
provider: Arc<dyn LLMProvider>,
) -> Result<Self, AppError>
pub async fn new_with_provider( bamboo_home_dir: PathBuf, config: Config, provider: Arc<dyn LLMProvider>, ) -> Result<Self, AppError>
Create unified app state with a specific provider
Allows injecting a custom LLM provider instead of creating one from configuration. Useful for testing and custom deployments.
§Arguments
bamboo_home_dir- Bamboo home directory containing all application dataconfig- Application configurationprovider- Pre-configured LLM provider implementation
§Returns
A fully initialized AppState with the provided provider.
Source§impl AppState
impl AppState
Sourcepub async fn reload_provider(&self) -> Result<(), LLMError>
pub async fn reload_provider(&self) -> Result<(), LLMError>
Reload the provider based on current configuration
Re-reads the configuration and creates a new LLM provider instance, allowing runtime switching of providers or models.
§Returns
Ok(()) if the provider was successfully reloaded.
§Errors
Returns an error if:
- Configuration cannot be read
- Provider initialization fails (e.g., invalid API key)
§Example
use bamboo_server::app_state::AppState;
use std::path::PathBuf;
#[tokio::main]
async fn main() {
let state = AppState::new(PathBuf::from("/path/to/.bamboo"))
.await
.expect("failed to initialize app state");
// User updated config file...
state.reload_provider().await.expect("Provider reload failed");
}Sourcepub async fn reload_config(&self) -> Config
pub async fn reload_config(&self) -> Config
Reload the configuration from file
Reads the configuration file again and updates the in-memory
config. Note: This does NOT automatically reload the provider;
call reload_provider() afterwards if needed.
§Returns
The newly loaded configuration.
§Example
use bamboo_server::app_state::AppState;
use std::path::PathBuf;
#[tokio::main]
async fn main() {
let state = AppState::new(PathBuf::from("/path/to/.bamboo"))
.await
.expect("failed to initialize app state");
// Reload config from disk
let new_config = state.reload_config().await;
// Optionally reload provider with new config
state.reload_provider().await.ok();
}Sourcepub async fn persist_config(&self) -> Result<()>
pub async fn persist_config(&self) -> Result<()>
Persist the current in-memory config to disk ({app_data_dir}/config.json).
This is the single “exit” for configuration writes in the server runtime.
Sourcepub async fn update_config<F>(
&self,
update: F,
effects: ConfigUpdateEffects,
) -> Result<Config, AppError>
pub async fn update_config<F>( &self, update: F, effects: ConfigUpdateEffects, ) -> Result<Config, AppError>
Unified config update entrypoint.
Invariants:
- Update in-memory first
- Persist to disk
- Apply runtime side-effects last (provider reload, MCP reconcile)
Sourcepub async fn replace_config(
&self,
new_config: Config,
effects: ConfigUpdateEffects,
) -> Result<Config, AppError>
pub async fn replace_config( &self, new_config: Config, effects: ConfigUpdateEffects, ) -> Result<Config, AppError>
Replace the full config (used for JSON merge endpoints).
Source§impl AppState
impl AppState
Sourcepub async fn save_session(&self, session: &Session)
pub async fn save_session(&self, session: &Session)
Save a complete session to persistent storage
Writes the session metadata to the storage backend.
§Arguments
session- Session object to save
Source§impl AppState
impl AppState
Sourcepub async fn get_provider(&self) -> Arc<dyn LLMProvider> ⓘ
pub async fn get_provider(&self) -> Arc<dyn LLMProvider> ⓘ
Get a clone of the current provider
Returns a thread-safe reference to the current LLM provider. This is the preferred way to access the provider for making requests.
§Returns
An Arc reference to the current provider implementation.
§Example
use bamboo_server::app_state::AppState;
use std::path::PathBuf;
#[tokio::main]
async fn main() {
let state = AppState::new(PathBuf::from("/path/to/.bamboo"))
.await
.expect("failed to initialize app state");
let provider = state.get_provider().await;
// Use provider to make LLM requests...
}Sourcepub fn get_provider_for_model_ref(
&self,
target: &ProviderModelRef,
) -> Result<Arc<dyn LLMProvider>, AppError>
pub fn get_provider_for_model_ref( &self, target: &ProviderModelRef, ) -> Result<Arc<dyn LLMProvider>, AppError>
Get a provider for a specific [ProviderModelRef].
Used when features.provider_model_ref is enabled to route requests
to the correct provider based on the model reference.
Sourcepub async fn get_provider_for_endpoint(
&self,
provider_name: &str,
) -> Result<Arc<dyn LLMProvider>, AppError>
pub async fn get_provider_for_endpoint( &self, provider_name: &str, ) -> Result<Arc<dyn LLMProvider>, AppError>
Get the appropriate provider for a named provider endpoint (e.g., “openai”, “anthropic”).
Uses the registry when the provider_model_ref feature flag is enabled,
otherwise falls back to the default provider.
Sourcepub async fn shutdown(&self)
pub async fn shutdown(&self)
Shutdown all MCP servers gracefully
Sends shutdown signals to all running MCP server processes and waits for them to terminate cleanly.
This should be called during application shutdown to ensure MCP servers are not left running as orphaned processes.
Sourcepub fn tools_for(&self, surface: ToolSurface) -> Arc<dyn ToolExecutor> ⓘ
pub fn tools_for(&self, surface: ToolSurface) -> Arc<dyn ToolExecutor> ⓘ
Get the tool executor for a specific surface variant.
Use ToolSurface::Root for primary sessions,
ToolSurface::Child for child sessions, etc.
Sourcepub fn get_all_tool_schemas(&self) -> Vec<ToolSchema>
pub fn get_all_tool_schemas(&self) -> Vec<ToolSchema>
Get all tool schemas from the composite tool executor
Returns schemas for both built-in tools and MCP-provided tools. These schemas are used to inform the LLM about available tools.
§Returns
Vector of tool schemas in Anthropic’s tool definition format.
Source§impl AppState
impl AppState
Sourcepub async fn get_session_event_sender(
&self,
session_id: &str,
) -> Sender<AgentEvent>
pub async fn get_session_event_sender( &self, session_id: &str, ) -> Sender<AgentEvent>
Get (or create) a long-lived session event sender for a session id.
This stream is intended for UI consumption and background activity; it should remain available even when no agent execution is running.
Source§impl AppState
impl AppState
Sourcepub async fn load_session(&self, session_id: &str) -> Option<Session>
pub async fn load_session(&self, session_id: &str) -> Option<Session>
Load a session from memory cache, falling back to persistent storage.
Returns None if the session does not exist in either tier.
Sourcepub async fn load_or_create_session(
&self,
session_id: &str,
model: &str,
) -> Session
pub async fn load_or_create_session( &self, session_id: &str, model: &str, ) -> Session
Load a session, creating a new one if it doesn’t exist.
Memory cache → storage → new Session::new(session_id, model).
Sourcepub async fn load_session_merged(&self, session_id: &str) -> Option<Session>
pub async fn load_session_merged(&self, session_id: &str) -> Option<Session>
Load a session, merging memory and storage using a preference heuristic.
Prefers the storage version when:
- memory lacks a
pending_questionbut storage has one - storage session has a newer
updated_at
Sourcepub async fn save_and_cache_session(&self, session: &Session)
pub async fn save_and_cache_session(&self, session: &Session)
Persist session to storage and update the in-memory cache.