gemini_adk_rs/planners/
built_in.rs

1//! Built-in planner — delegates planning to the model's native capabilities.
2//!
3//! When the model supports native planning (e.g., Gemini with thinking),
4//! this planner injects minimal instructions and lets the model plan natively.
5
6use async_trait::async_trait;
7
8use super::{Planner, PlannerError};
9use crate::llm::LlmRequest;
10
11/// Built-in planner that leverages the model's native planning capabilities.
12///
13/// This is a lightweight planner that adds a simple planning instruction
14/// to encourage the model to think step-by-step before acting.
15#[derive(Debug, Clone, Default)]
16pub struct BuiltInPlanner {
17    /// Optional custom planning instruction override.
18    custom_instruction: Option<String>,
19}
20
21impl BuiltInPlanner {
22    /// Create a new built-in planner with default instructions.
23    pub fn new() -> Self {
24        Self::default()
25    }
26
27    /// Create a built-in planner with a custom planning instruction.
28    pub fn with_instruction(instruction: impl Into<String>) -> Self {
29        Self {
30            custom_instruction: Some(instruction.into()),
31        }
32    }
33}
34
35const DEFAULT_PLANNING_INSTRUCTION: &str = "\
36Before taking any action, think step by step about what you need to do. \
37Create a brief plan, then execute it. If you need to adjust your plan based \
38on new information, explain your reasoning before changing course.";
39
40#[async_trait]
41impl Planner for BuiltInPlanner {
42    fn build_planning_instruction(
43        &self,
44        _request: &LlmRequest,
45    ) -> Result<Option<String>, PlannerError> {
46        Ok(Some(self.custom_instruction.clone().unwrap_or_else(|| {
47            DEFAULT_PLANNING_INSTRUCTION.to_string()
48        })))
49    }
50
51    fn process_planning_response(
52        &self,
53        _response_text: &str,
54    ) -> Result<Option<String>, PlannerError> {
55        // Built-in planner doesn't filter responses — model handles planning natively
56        Ok(None)
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn default_instruction() {
66        let planner = BuiltInPlanner::new();
67        let request = LlmRequest::default();
68        let instruction = planner.build_planning_instruction(&request).unwrap();
69        assert!(instruction.is_some());
70        assert!(instruction.unwrap().contains("step by step"));
71    }
72
73    #[test]
74    fn custom_instruction() {
75        let planner = BuiltInPlanner::with_instruction("Plan carefully");
76        let request = LlmRequest::default();
77        let instruction = planner.build_planning_instruction(&request).unwrap();
78        assert_eq!(instruction.unwrap(), "Plan carefully");
79    }
80
81    #[test]
82    fn response_passthrough() {
83        let planner = BuiltInPlanner::new();
84        let result = planner.process_planning_response("some response").unwrap();
85        assert!(result.is_none());
86    }
87}