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