Skip to main content

bamboo_engine/runtime/managers/adapters/
llm.rs

1use std::sync::Arc;
2
3use async_trait::async_trait;
4use bamboo_agent_core::tools::ToolSchema;
5use bamboo_agent_core::{AgentError, AgentEvent, Session};
6use bamboo_llm::LLMProvider;
7use tokio::sync::mpsc;
8use tokio_util::sync::CancellationToken;
9
10use crate::runtime::config::AgentLoopConfig;
11use crate::runtime::managers::llm::{LlmManager, LlmRoundOutput};
12
13/// Default LLM manager that delegates to existing runner functions.
14pub struct DefaultLlmManager {
15    llm: Arc<dyn LLMProvider>,
16}
17
18impl DefaultLlmManager {
19    pub fn new(llm: Arc<dyn LLMProvider>) -> Self {
20        Self { llm }
21    }
22}
23
24#[async_trait]
25impl LlmManager for DefaultLlmManager {
26    #[allow(clippy::too_many_arguments)]
27    async fn execute_round(
28        &self,
29        session: &mut Session,
30        config: &AgentLoopConfig,
31        event_tx: &mpsc::Sender<AgentEvent>,
32        cancel_token: &CancellationToken,
33        session_id: &str,
34        model_name: &str,
35        tool_schemas: &[ToolSchema],
36    ) -> Result<LlmRoundOutput, AgentError> {
37        let result = crate::runtime::runner::round_lifecycle::execute_llm_round(
38            session,
39            config,
40            &self.llm,
41            event_tx,
42            cancel_token,
43            session_id,
44            model_name,
45            tool_schemas,
46            None,
47            // This dormant lifecycle adapter does not yet carry the trusted
48            // execution-local prompt-memory provenance. `None` is unsupported
49            // coverage, not a zero-exposure observation; #1077 covers the
50            // canonical runner pipeline only.
51            None,
52        )
53        .await?;
54
55        if let Some(error) = result.terminal_validation_error {
56            return Err(error);
57        }
58
59        let (content, reasoning_content, tool_calls) = {
60            let stream = &result.stream_output;
61            (
62                stream.content.clone(),
63                stream.reasoning_content.clone(),
64                stream.tool_calls.clone(),
65            )
66        };
67
68        Ok(LlmRoundOutput {
69            content,
70            reasoning_content,
71            tool_calls,
72            prompt_tokens: result.prompt_tokens,
73            completion_tokens: result.completion_tokens,
74            response_id: None,
75            round_usage: result.attempt_usage,
76        })
77    }
78
79    async fn attempt_overflow_recovery(
80        &self,
81        session: &mut Session,
82        config: &AgentLoopConfig,
83        session_id: &str,
84        tool_schemas: &[ToolSchema],
85        event_tx: &mpsc::Sender<AgentEvent>,
86    ) -> Result<bool, AgentError> {
87        let model_name = config.model_name.as_deref().unwrap_or("unknown");
88        let request_tool_schemas =
89            crate::runtime::runner::round_lifecycle::request_tool_schemas_for_session(
90                session,
91                &self.llm,
92                model_name,
93                tool_schemas,
94            )
95            .await;
96
97        crate::runtime::runner::round_lifecycle::force_overflow_context_recovery(
98            session,
99            config,
100            model_name,
101            session_id,
102            request_tool_schemas.as_ref(),
103            &self.llm,
104            Some(event_tx),
105        )
106        .await
107    }
108}