1use std::{
2 collections::VecDeque,
3 sync::{Arc, Mutex, MutexGuard},
4};
5
6use crate::{ContentBlock, Message, error::RuntimeError, runtime::RunOptions};
7
8use super::Agent;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
12pub enum QueueMode {
13 All,
15 #[default]
17 OneAtATime,
18}
19
20#[derive(Default)]
21struct SteeringQueues {
22 steer: VecDeque<Vec<ContentBlock>>,
23 follow_up: VecDeque<Vec<ContentBlock>>,
24 steer_mode: QueueMode,
25 follow_up_mode: QueueMode,
26}
27
28#[derive(Clone, Default)]
36pub struct SteeringHandle {
37 queues: Arc<Mutex<SteeringQueues>>,
38}
39
40impl SteeringHandle {
41 pub(crate) fn new() -> Self {
42 Self::default()
43 }
44
45 pub fn steer(&self, content: impl Into<Vec<ContentBlock>>) {
47 let content = content.into();
48 if !content.is_empty() {
49 self.lock().steer.push_back(content);
50 }
51 }
52
53 pub fn follow_up(&self, content: impl Into<Vec<ContentBlock>>) {
55 let content = content.into();
56 if !content.is_empty() {
57 self.lock().follow_up.push_back(content);
58 }
59 }
60
61 pub fn clear_steer(&self) {
63 self.lock().steer.clear();
64 }
65
66 pub fn clear_follow_up(&self) {
68 self.lock().follow_up.clear();
69 }
70
71 pub fn has_pending(&self) -> bool {
73 let queues = self.lock();
74 !queues.steer.is_empty() || !queues.follow_up.is_empty()
75 }
76
77 pub fn set_steer_mode(&self, mode: QueueMode) {
79 self.lock().steer_mode = mode;
80 }
81
82 pub fn set_follow_up_mode(&self, mode: QueueMode) {
84 self.lock().follow_up_mode = mode;
85 }
86
87 pub(crate) fn has_steer(&self) -> bool {
88 !self.lock().steer.is_empty()
89 }
90
91 pub(crate) fn has_follow_up(&self) -> bool {
92 !self.lock().follow_up.is_empty()
93 }
94
95 fn drain_steer(&self) -> Vec<Vec<ContentBlock>> {
96 let mut queues = self.lock();
97 let mode = queues.steer_mode;
98 drain(&mut queues.steer, mode)
99 }
100
101 fn drain_follow_up(&self) -> Vec<Vec<ContentBlock>> {
102 let mut queues = self.lock();
103 let mode = queues.follow_up_mode;
104 drain(&mut queues.follow_up, mode)
105 }
106
107 fn prepend_steer(&self, entries: Vec<Vec<ContentBlock>>) {
108 prepend(&mut self.lock().steer, entries);
109 }
110
111 fn prepend_follow_up(&self, entries: Vec<Vec<ContentBlock>>) {
112 prepend(&mut self.lock().follow_up, entries);
113 }
114
115 fn lock(&self) -> MutexGuard<'_, SteeringQueues> {
116 self.queues
117 .lock()
118 .unwrap_or_else(|poisoned| poisoned.into_inner())
119 }
120}
121
122impl Agent {
123 pub fn steering_handle(&self) -> SteeringHandle {
126 self.steering.clone()
127 }
128
129 pub fn steer(&self, content: impl Into<Vec<ContentBlock>>) {
131 self.steering.steer(content);
132 }
133
134 pub fn follow_up(&self, content: impl Into<Vec<ContentBlock>>) {
136 self.steering.follow_up(content);
137 }
138
139 pub async fn run_queued(&mut self, options: RunOptions) -> Result<Message, RuntimeError> {
145 let Some(content) = self.drain_steer() else {
146 return Err(RuntimeError::OperationDenied(
147 "no queued steering input is available".to_string(),
148 ));
149 };
150
151 let result = self.run(content, options).await;
152 if result.is_err() {
153 self.requeue_inflight_steering();
157 }
158 result
159 }
160
161 pub(super) fn has_pending_steer(&self) -> bool {
162 self.steering.has_steer()
163 }
164
165 pub(super) fn has_pending_follow_up(&self) -> bool {
166 self.steering.has_follow_up()
167 }
168
169 pub(super) fn drain_steer(&mut self) -> Option<Vec<ContentBlock>> {
170 let entries = self.steering.drain_steer();
171 if entries.is_empty() {
172 return None;
173 }
174 let content = flatten(&entries);
175 self.inflight_steer.extend(entries);
176 Some(content)
177 }
178
179 pub(super) fn drain_follow_up(&mut self) -> Option<Vec<ContentBlock>> {
180 let entries = self.steering.drain_follow_up();
181 if entries.is_empty() {
182 return None;
183 }
184 let content = flatten(&entries);
185 self.inflight_follow_up.extend(entries);
186 Some(content)
187 }
188
189 pub(super) fn clear_inflight_steering(&mut self) {
190 self.inflight_steer.clear();
191 self.inflight_follow_up.clear();
192 }
193
194 pub(super) fn requeue_inflight_steering(&mut self) {
195 self.steering
196 .prepend_steer(std::mem::take(&mut self.inflight_steer));
197 self.steering
198 .prepend_follow_up(std::mem::take(&mut self.inflight_follow_up));
199 }
200}
201
202fn drain(queue: &mut VecDeque<Vec<ContentBlock>>, mode: QueueMode) -> Vec<Vec<ContentBlock>> {
203 match mode {
204 QueueMode::All => queue.drain(..).collect(),
205 QueueMode::OneAtATime => queue.pop_front().into_iter().collect(),
206 }
207}
208
209fn prepend(queue: &mut VecDeque<Vec<ContentBlock>>, entries: Vec<Vec<ContentBlock>>) {
210 for entry in entries.into_iter().rev() {
211 queue.push_front(entry);
212 }
213}
214
215fn flatten(entries: &[Vec<ContentBlock>]) -> Vec<ContentBlock> {
216 entries.iter().flatten().cloned().collect()
217}