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