agent_base/engine/runtime/
mod.rs1use std::sync::Arc;
2
3use tokio_util::sync::CancellationToken;
4
5use crate::engine::AgentSession;
6use crate::engine::session_store::SessionStore;
7use crate::types::{
8 AgentConfig, AgentError, AgentResult, CheckpointData, MessageRole, RunOutcome, RuntimeEvent,
9 SessionId, TurnContext,
10};
11
12use super::approval::ApprovalHandler;
13
14mod event_bus;
15pub(crate) use event_bus::EventBus;
16mod llm_engine;
17mod message_queue;
18mod plan_runner;
19mod react_loop;
20mod session_manager;
21mod tool_engine;
22
23pub(super) const DEFAULT_MAX_TURNS: u32 = 50;
24
25pub use llm_engine::LlmEngine;
26pub use message_queue::QueueMode;
27pub(crate) use plan_runner::RuntimeCore;
28pub use session_manager::SessionManager;
29pub(crate) use tool_engine::ToolEngine;
30
31#[derive(Clone)]
32pub struct AgentRuntime {
33 pub(crate) runner: Arc<RuntimeCore>,
34}
35
36impl AgentRuntime {
37 pub async fn create_session(&self) -> SessionId {
38 let config = self.runner.config.read().await;
39 self.runner
40 .session_manager
41 .create_session(config.system_prompt.as_deref())
42 .await
43 }
44
45 pub async fn restore_session(&self, session_id: &SessionId) -> Option<AgentSession> {
46 self.runner
47 .session_manager
48 .restore_session(session_id)
49 .await
50 }
51
52 pub async fn session(&self, session_id: &SessionId) -> Option<AgentSession> {
53 self.runner.session_manager.session(session_id).await
54 }
55
56 pub async fn session_or_err(&self, session_id: &SessionId) -> AgentResult<AgentSession> {
57 self.runner.session_manager.session_or_err(session_id).await
58 }
59
60 pub async fn with_session_mut<F, R>(&self, session_id: &SessionId, f: F) -> AgentResult<R>
61 where
62 F: FnOnce(&mut AgentSession) -> R,
63 {
64 self.runner
65 .session_manager
66 .with_session_mut(session_id, f)
67 .await
68 }
69
70 pub fn emit_event(&self, event: RuntimeEvent) {
71 self.runner.event_bus.emit(event);
72 }
73
74 pub fn subscribe_runtime_events(&self) -> tokio::sync::broadcast::Receiver<RuntimeEvent> {
81 self.runner.event_bus.subscribe()
82 }
83
84 pub fn session_manager(&self) -> &SessionManager {
85 &self.runner.session_manager
86 }
87
88 pub fn llm_engine(&self) -> &LlmEngine {
89 &self.runner.llm_engine
90 }
91
92 pub fn client(&self) -> Arc<dyn crate::llm::StreamClient> {
93 self.runner.llm_engine.get_client()
94 }
95
96 pub fn set_client(&mut self, client: Arc<dyn crate::llm::StreamClient>) {
99 self.runner.llm_engine.set_client(client);
100 }
101
102 pub fn tools_mut(&self) -> Arc<tokio::sync::RwLock<crate::tool::ToolRegistry>> {
103 self.runner.tool_engine.tools_arc()
104 }
105
106 pub fn inject_framework_deps(&self, tools: &crate::tool::ToolRegistry) {
109 self.runner.tool_engine.inject_event_bus_into(tools);
110 }
111
112 pub fn config(&self) -> tokio::sync::RwLockReadGuard<'_, AgentConfig> {
113 self.runner.config.blocking_read()
114 }
115
116 pub async fn set_reasoning_effort(&self, effort: crate::llm::ReasoningEffort) {
118 let mut config = self.runner.config.write().await;
119 let mut reasoning = config.reasoning.take().unwrap_or_default();
120 reasoning.effort = Some(effort);
121 config.reasoning = Some(reasoning);
122 }
123
124 pub fn set_reasoning_effort_sync(&self, effort: crate::llm::ReasoningEffort) {
126 let mut config = self.runner.config.blocking_write();
127 let mut reasoning = config.reasoning.take().unwrap_or_default();
128 reasoning.effort = Some(effort);
129 config.reasoning = Some(reasoning);
130 }
131
132 pub fn approval_handler(&self) -> Option<&Arc<dyn ApprovalHandler>> {
133 self.runner.tool_engine.approval_handler()
134 }
135
136 pub async fn cached_approval(&self, session_id: &SessionId, action_key: &str) -> bool {
137 self.runner
138 .session_manager
139 .cached_approval(session_id, action_key)
140 .await
141 }
142
143 pub async fn cache_approval(&self, session_id: &SessionId, action_key: String) {
144 self.runner
145 .session_manager
146 .cache_approval(session_id, action_key)
147 .await
148 }
149
150 pub async fn save_checkpoint(
151 &self,
152 session_id: &SessionId,
153 checkpoint: CheckpointData,
154 ) -> AgentResult<()> {
155 self.emit_event(RuntimeEvent::Checkpoint {
156 session_id: session_id.clone(),
157 checkpoint,
158 agent_id: None,
159 trace_id: None,
160 });
161 Ok(())
162 }
163
164 pub async fn load_checkpoint(
165 &self,
166 _session_id: &SessionId,
167 _checkpoint: &CheckpointData,
168 ) -> AgentResult<Option<CheckpointData>> {
169 Ok(None)
170 }
171
172 pub async fn run<F>(&self, session_id: SessionId, on_event: F) -> AgentResult<RunOutcome>
173 where
174 F: FnMut(RuntimeEvent) -> AgentResult<()> + Send,
175 {
176 self.runner.run(session_id, on_event).await
177 }
178
179 pub async fn run_turn<F>(
180 &self,
181 session_id: SessionId,
182 user_input: &str,
183 on_event: F,
184 ) -> AgentResult<RunOutcome>
185 where
186 F: FnMut(RuntimeEvent) -> AgentResult<()> + Send,
187 {
188 self.runner.run_turn(session_id, user_input, on_event).await
189 }
190
191 pub async fn run_turn_collect(
192 &self,
193 session_id: SessionId,
194 user_input: &str,
195 ) -> AgentResult<(Vec<RuntimeEvent>, RunOutcome)> {
196 self.runner.run_turn_collect(session_id, user_input).await
197 }
198
199 pub async fn add_user_message(
200 &self,
201 session_id: &SessionId,
202 text: impl Into<String>,
203 ) -> AgentResult<()> {
204 let text = text.into();
205 self.with_session_mut(session_id, |session| {
206 session.push_message(MessageRole::User, &text);
207 })
208 .await
209 }
210
211 pub async fn add_system_message(
212 &self,
213 session_id: &SessionId,
214 text: impl Into<String>,
215 ) -> AgentResult<()> {
216 let text = text.into();
217 self.with_session_mut(session_id, |session| {
218 session.push_message(MessageRole::System, &text);
219 })
220 .await
221 }
222
223 pub async fn add_tool_result(
224 &self,
225 session_id: &SessionId,
226 tool_call_id: &str,
227 summary: impl Into<String>,
228 ) -> AgentResult<()> {
229 let summary = summary.into();
230 self.with_session_mut(session_id, |session| {
231 session.push_tool_result(tool_call_id, summary.clone());
232 })
233 .await
234 }
235
236 pub async fn get_messages(
237 &self,
238 session_id: &SessionId,
239 ) -> AgentResult<Vec<crate::types::ChatMessage>> {
240 let session = self.session_or_err(session_id).await?;
241 Ok(session.chat_messages().to_vec())
242 }
243
244 pub async fn set_messages(
249 &self,
250 session_id: &SessionId,
251 messages: Vec<crate::types::ChatMessage>,
252 ) -> AgentResult<()> {
253 self.with_session_mut(session_id, |session| session.set_chat_messages(messages))
254 .await?
255 .map_err(AgentError::internal)
256 }
257
258 pub async fn validate_session(&self, session_id: &SessionId) -> AgentResult<()> {
259 if self
260 .runner
261 .session_manager
262 .session(session_id)
263 .await
264 .is_none()
265 {
266 return Err(AgentError::session_not_found(session_id.id));
267 }
268 Ok(())
269 }
270
271 pub fn session_store(&self) -> Arc<dyn SessionStore> {
272 self.runner.session_manager.session_store().clone()
273 }
274
275 pub fn on_turn_end<F>(&self, f: F)
282 where
283 F: Fn(&TurnContext) + Send + Sync + 'static,
284 {
285 self.runner
286 .turn_end_callbacks
287 .write()
288 .unwrap()
289 .push(Arc::new(f));
290 }
291
292 pub fn cancel(&self) {
297 self.runner.cancel();
298 }
299
300 pub fn reset_cancel(&self) {
302 self.runner.reset_cancel();
303 }
304
305 pub fn cancel_token(&self) -> CancellationToken {
307 self.runner.cancel_token()
308 }
309
310 pub fn is_cancelled(&self) -> bool {
312 self.runner.is_cancelled()
313 }
314
315 pub fn steer(&self, message: String) {
320 self.runner.message_queue.steer(message);
321 }
322
323 pub fn follow_up(&self, message: String) {
326 self.runner.message_queue.follow_up(message);
327 }
328
329 pub async fn run_managed<F>(
338 &self,
339 session_id: SessionId,
340 user_input: &str,
341 on_event: F,
342 ) -> AgentResult<RunOutcome>
343 where
344 F: FnMut(RuntimeEvent) -> AgentResult<()> + Send,
345 {
346 self.runner
347 .run_managed(session_id, user_input, on_event)
348 .await
349 }
350
351 pub fn set_queue_mode(&self, mode: crate::engine::runtime::message_queue::QueueMode) {
353 self.runner.message_queue.set_mode(mode);
354 }
355}