Skip to main content

bamboo_engine/runtime/
config.rs

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