Skip to main content

bamboo_engine/
app_context.rs

1//! Dependency-inversion context for the agent-session-orchestration cluster.
2//!
3//! The session/event/title/gold modules that live in this crate need a handful
4//! of shared application resources (sessions cache, storage, persistence,
5//! providers, the per-session event senders, and the account change-feed sink).
6//! Historically those were reached through the server's `AppState`, which
7//! coupled the cluster to the actix-web server crate.
8//!
9//! [`AgentSessionContext`] inverts that dependency: the cluster depends only on
10//! this trait, and the server implements it on `AppState`. This keeps the
11//! orchestration code free of any HTTP / server types.
12//!
13//! Accessor return types are deliberately the *exact* field types `AppState`
14//! already exposes, so the server-side impl is a thin delegation and no copying
15//! is introduced. Provider lookups return `Option` (the server's fallible
16//! lookups discard the error type, which is actix-coupled).
17
18use std::collections::HashMap;
19use std::sync::Arc;
20
21use async_trait::async_trait;
22use tokio::sync::{broadcast, RwLock};
23
24use bamboo_agent_core::storage::Storage;
25use bamboo_agent_core::{AgentEvent, Session};
26use bamboo_domain::ProviderModelRef;
27use bamboo_llm::{Config, LLMError, LLMProvider, ProviderRegistry};
28use bamboo_storage::LockedSessionStore;
29
30use crate::events::AccountEventSink;
31use crate::runtime::execution::runner_state::AgentRunner;
32
33#[cfg(test)]
34#[path = "app_context/model_routing_test_support.rs"]
35pub(crate) mod model_routing_test_support;
36
37/// Shared application context the agent-session-orchestration cluster depends
38/// on, in place of the server's concrete `AppState`.
39///
40/// Implemented by the server on `AppState`. Every accessor mirrors an existing
41/// `AppState` field or method; the trait exists solely to invert the
42/// cluster→server dependency.
43#[async_trait]
44pub trait AgentSessionContext: Send + Sync {
45    /// In-memory session cache.
46    fn sessions(&self) -> &crate::SessionCache;
47
48    /// Persistent session storage backend.
49    fn storage(&self) -> &Arc<dyn Storage>;
50
51    /// Per-session write-serialising persistence layer.
52    fn persistence(&self) -> &Arc<LockedSessionStore>;
53
54    /// Active agent runners, keyed by session id.
55    fn agent_runners(&self) -> &Arc<RwLock<HashMap<String, AgentRunner>>>;
56
57    /// Account-wide durable change-feed sink.
58    fn account_sink(&self) -> &Arc<AccountEventSink>;
59
60    /// Hot-reloadable application configuration.
61    fn config(&self) -> &Arc<RwLock<Config>>;
62
63    /// Multi-provider registry.
64    fn provider_registry(&self) -> &Arc<ProviderRegistry>;
65
66    /// Get (or create) the long-lived broadcast sender for a session's events.
67    async fn get_session_event_sender(&self, session_id: &str) -> broadcast::Sender<AgentEvent>;
68
69    /// Load a session, merging the in-memory and on-disk copies.
70    async fn load_session_merged(&self, session_id: &str) -> Option<Session>;
71
72    /// Persist a session and refresh the in-memory cache.
73    async fn save_and_cache_session(&self, session: &mut Session);
74
75    /// Get the always-current default provider handle.
76    async fn get_provider(&self) -> Arc<dyn LLMProvider>;
77
78    /// Route to a provider for a specific provider/model ref.
79    ///
80    /// Returns `None` on failure (the server's fallible lookup discards its
81    /// actix-coupled error type).
82    fn get_provider_for_model_ref(&self, target: &ProviderModelRef)
83        -> Option<Arc<dyn LLMProvider>>;
84
85    /// Route against a caller-held config snapshot. Standalone embedders retain
86    /// their existing override; hosts may enforce config-managed provenance.
87    /// This synchronous method must not attempt to reacquire the config lock.
88    fn get_provider_for_model_ref_with_config(
89        &self,
90        _config: &Config,
91        target: &ProviderModelRef,
92    ) -> Result<Option<Arc<dyn LLMProvider>>, LLMError> {
93        Ok(self.get_provider_for_model_ref(target))
94    }
95
96    /// Hosts with config-bound synchronous routing opt in. The default keeps
97    /// legacy embedder callbacks outside the config lock.
98    fn resolve_provider_ref_under_config_lock(&self) -> bool {
99        false
100    }
101
102    /// Resolve a provider for a named provider endpoint.
103    ///
104    /// Returns `None` on failure (see [`Self::get_provider_for_model_ref`]).
105    async fn get_provider_for_endpoint(&self, provider_name: &str) -> Option<Arc<dyn LLMProvider>>;
106
107    /// Try to claim the title-generation slot for `session_id`.
108    /// Returns `true` on success, `false` if generation is already in flight.
109    fn title_gen_acquire(&self, session_id: &str) -> bool;
110
111    /// Release the title-generation slot for `session_id`. Idempotent.
112    fn title_gen_release(&self, session_id: &str);
113}