Skip to main content

bamboo_engine/runtime/
config.rs

1use std::collections::BTreeSet;
2use std::path::PathBuf;
3use std::sync::Arc;
4
5use crate::metrics::MetricsCollector;
6use crate::skills::SkillManager;
7use bamboo_agent_core::composition::CompositionExecutor;
8use bamboo_agent_core::storage::AttachmentReader;
9use bamboo_agent_core::storage::Storage;
10use bamboo_agent_core::tools::ToolSchema;
11use bamboo_compression::TokenBudget;
12use bamboo_domain::ReasoningEffort;
13use bamboo_domain::RuntimeSessionPersistence;
14use bamboo_infrastructure::config::PermissionMode;
15use bamboo_infrastructure::LLMProvider;
16use bamboo_infrastructure::MemoryConfig;
17use bamboo_tools::ToolRegistry;
18
19#[derive(Clone, Default)]
20pub struct AuxiliaryModelConfig {
21    pub fast_model_name: Option<String>,
22    pub fast_model_provider: Option<Arc<dyn LLMProvider>>,
23    pub background_model_name: Option<String>,
24    pub planning_model_name: Option<String>,
25    pub search_model_name: Option<String>,
26    pub summarization_model_name: Option<String>,
27    pub background_model_provider: Option<Arc<dyn LLMProvider>>,
28    pub summarization_model_provider: Option<Arc<dyn LLMProvider>>,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum ImageFallbackMode {
33    Placeholder,
34    Error,
35    Ocr,
36    /// Use a vision-capable LLM to describe the image, then replace the image
37    /// with the textual description so that text-only models can understand
38    /// the content.
39    Vision,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub struct ImageFallbackConfig {
44    pub mode: ImageFallbackMode,
45    /// Vision model name for `Vision` mode. Falls back to the session's main model
46    /// when `None`.
47    pub vision_model: Option<String>,
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub struct PromptMemoryFlags {
52    pub project_prompt_injection: bool,
53    pub relevant_recall: bool,
54    pub relevant_recall_rerank: bool,
55    pub project_first_dream: bool,
56}
57
58impl Default for PromptMemoryFlags {
59    fn default() -> Self {
60        Self {
61            project_prompt_injection: true,
62            relevant_recall: true,
63            relevant_recall_rerank: false,
64            project_first_dream: true,
65        }
66    }
67}
68
69impl From<&MemoryConfig> for PromptMemoryFlags {
70    fn from(value: &MemoryConfig) -> Self {
71        Self {
72            project_prompt_injection: value.project_prompt_injection,
73            relevant_recall: value.relevant_recall,
74            relevant_recall_rerank: value.relevant_recall_rerank,
75            project_first_dream: value.project_first_dream,
76        }
77    }
78}
79
80/// Configuration for the agent loop.
81pub struct AgentLoopConfig {
82    pub max_rounds: usize,
83    pub system_prompt: Option<String>,
84    /// Skill IDs that are disabled globally for this execution.
85    pub disabled_skill_ids: BTreeSet<String>,
86    /// Optional explicit skill selection for this execution.
87    /// When set, only these skill IDs are considered for skill context and allowlists.
88    pub selected_skill_ids: Option<Vec<String>>,
89    /// Optional active skill mode for this execution.
90    ///
91    /// When set, skill discovery prefers `skills-<mode>` directories over generic
92    /// directories for the same skill id.
93    pub selected_skill_mode: Option<String>,
94    pub additional_tool_schemas: Vec<ToolSchema>,
95    pub tool_registry: Arc<ToolRegistry>,
96    pub composition_executor: Option<Arc<CompositionExecutor>>,
97    pub skill_manager: Option<Arc<SkillManager>>,
98    /// If true, skip appending the initial user message (already present in session).
99    pub skip_initial_user_message: bool,
100    /// Optional storage for persisting session changes
101    pub storage: Option<Arc<dyn Storage>>,
102    /// Optional runtime persistence for non-authoritative session saves.
103    /// When set, engine save sites use this instead of `storage` for writes.
104    pub persistence: Option<Arc<dyn RuntimeSessionPersistence>>,
105    /// Optional attachment reader for resolving `bamboo-attachment://...` references
106    /// into `data:` URLs for upstream providers. This must not mutate session storage.
107    pub attachment_reader: Option<Arc<dyn AttachmentReader>>,
108    /// Optional asynchronous metrics collector
109    pub metrics_collector: Option<MetricsCollector>,
110    /// Model name used for metrics attribution
111    pub model_name: Option<String>,
112    /// Fast/cheap model for lightweight tasks (task evaluation, search, etc.).
113    ///
114    /// Call sites may fall back to `model_name` when this is unset.
115    pub fast_model_name: Option<String>,
116    /// Optional provider override for lightweight fast-model LLM calls.
117    pub fast_model_provider: Option<Arc<dyn LLMProvider>>,
118    /// Fast/cheap model for memory/background tasks.
119    ///
120    /// This must not silently fall back to the main interaction model.
121    pub background_model_name: Option<String>,
122
123    /// Model for planning/coordination tasks (task decomposition, architecture).
124    /// Falls back to `model_name` when unset.
125    pub planning_model_name: Option<String>,
126    /// Model for search/navigation tasks (grep, file listing, symbol resolution).
127    /// Falls back to `fast_model_name` when unset.
128    pub search_model_name: Option<String>,
129    /// Custom instructions for conversation summarization, injected into the
130    /// LLM summary prompt. Lets users control what the summary focuses on.
131    ///
132    /// Resolution order: session-level > config-level > built-in defaults.
133    pub compression_instructions: Option<String>,
134    /// Dedicated model for summarization. Falls back to `background_model_name`.
135    pub summarization_model_name: Option<String>,
136    /// Optional provider override for memory/background model LLM calls.
137    ///
138    /// When set, memory recall rerank and other memory/background tasks use this
139    /// provider instead of the shared agent loop provider.
140    pub background_model_provider: Option<Arc<dyn LLMProvider>>,
141    /// Optional provider override for summarization / context compression calls.
142    ///
143    /// When set, conversation/task summarization uses this provider instead of
144    /// the shared agent loop provider.
145    pub summarization_model_provider: Option<Arc<dyn LLMProvider>>,
146    /// Provider routing key used for provider-specific request behavior.
147    ///
148    /// In multi-instance mode this may be the instance id.
149    pub provider_name: Option<String>,
150    /// Underlying provider type (for example `openai`, `anthropic`, `copilot`).
151    ///
152    /// This is distinct from `provider_name` so provider-specific behavior can
153    /// remain correct when routing keys are instance ids.
154    pub provider_type: Option<String>,
155    /// Optional request-time reasoning effort override.
156    pub reasoning_effort: Option<ReasoningEffort>,
157    /// Bamboo application data directory (typically `~/.bamboo`).
158    ///
159    /// Used by runtime features that persist auxiliary artifacts outside the
160    /// session store, such as durable plan mode files under `~/.bamboo/plan`.
161    pub app_data_dir: Option<PathBuf>,
162    /// Tool names that should be excluded from schemas sent to the LLM.
163    pub disabled_tools: BTreeSet<String>,
164    /// Token budget for context management (optional, defaults to model's limits)
165    pub token_budget: Option<TokenBudget>,
166    /// Optional image fallback behavior applied to *LLM requests only* (never persisted).
167    ///
168    /// This is intended for text-only provider paths where image parts must be degraded
169    /// (placeholder / OCR / error) without leaking into stored session history or UI.
170    pub image_fallback: Option<ImageFallbackConfig>,
171    /// Feature flags controlling prompt-time memory injection behavior.
172    pub prompt_memory_flags: PromptMemoryFlags,
173    /// Maximum tool calls allowed per round (default: 80).
174    pub max_tool_calls_per_round: usize,
175    /// Maximum consecutive failures per tool before circuit breaker (default: 3).
176    pub max_consecutive_failures_per_tool: usize,
177    /// Tool names that require strict argument validation.
178    pub strict_argument_tool_names: Vec<String>,
179    /// Per-tool execution timeout in seconds (default: 120).
180    pub per_tool_timeout_secs: u64,
181    /// Parallel batch execution timeout in seconds (default: 300).
182    pub parallel_batch_timeout_secs: u64,
183    /// Permission mode for this execution (default: None = use PermissionConfig's mode).
184    pub permission_mode: Option<PermissionMode>,
185    /// Enable dynamic per-round model routing based on task complexity.
186    /// When true, the pipeline classifies complexity at each round end and
187    /// stores the result in session metadata.
188    pub features_dynamic_model_routing: bool,
189    /// Optional per-round resolver for auxiliary model settings that should
190    /// follow live global config rather than stay frozen for the whole run.
191    ///
192    /// The main chat model remains session/request scoped; this hook is only
193    /// for fast/background/planning/search/summarization helpers.
194    pub auxiliary_model_resolver:
195        Option<Arc<dyn Fn() -> AuxiliaryModelConfig + Send + Sync>>,
196}
197
198impl Default for AgentLoopConfig {
199    fn default() -> Self {
200        Self {
201            max_rounds: 200,
202            system_prompt: None,
203            disabled_skill_ids: BTreeSet::new(),
204            selected_skill_ids: None,
205            selected_skill_mode: None,
206            additional_tool_schemas: Vec::new(),
207            tool_registry: Arc::new(ToolRegistry::new()),
208            composition_executor: None,
209            skill_manager: None,
210            skip_initial_user_message: false,
211            storage: None,
212            persistence: None,
213            attachment_reader: None,
214            metrics_collector: None,
215            model_name: None,
216            fast_model_name: None,
217            fast_model_provider: None,
218            background_model_name: None,
219            planning_model_name: None,
220            search_model_name: None,
221            compression_instructions: None,
222            summarization_model_name: None,
223            background_model_provider: None,
224            summarization_model_provider: None,
225            provider_name: None,
226            provider_type: None,
227            reasoning_effort: None,
228            app_data_dir: None,
229            disabled_tools: BTreeSet::new(),
230            token_budget: None,
231            image_fallback: None,
232            prompt_memory_flags: PromptMemoryFlags::default(),
233            max_tool_calls_per_round: 80,
234            max_consecutive_failures_per_tool: 3,
235            strict_argument_tool_names: vec![
236                "Write".into(),
237                "Edit".into(),
238                "NotebookEdit".into(),
239                "apply_patch".into(),
240                "Bash".into(),
241                "Task".into(),
242                "SubAgent".into(),
243                "scheduler".into(),
244                "sub_session_manager".into(),
245                "session_note".into(),
246                "memory_note".into(),
247            ],
248            per_tool_timeout_secs: 120,
249            parallel_batch_timeout_secs: 300,
250            permission_mode: None,
251            features_dynamic_model_routing: false,
252            auxiliary_model_resolver: None,
253        }
254    }
255}
256
257#[cfg(test)]
258mod tests;