1use async_trait::async_trait;
9use std::collections::HashMap;
10use std::sync::Arc;
11
12use crate::error::{Error, Result};
13use crate::tools::{BoxFuture, ToolDefinition};
14use crate::types::{AgentResponse, ChatOptions, ChatResponse, Message};
15
16pub type Terminal<C> = Box<dyn FnOnce(C) -> BoxFuture<Result<C>> + Send>;
18
19#[async_trait]
21pub trait Middleware<C: Send + 'static>: Send + Sync {
22 async fn process(&self, ctx: C, next: Next<C>) -> Result<C>;
23}
24
25pub struct Next<C: Send + 'static> {
28 middlewares: Arc<Vec<Arc<dyn Middleware<C>>>>,
29 index: usize,
30 terminal: Option<Terminal<C>>,
31}
32
33impl<C: Send + 'static> Next<C> {
34 pub async fn run(mut self, ctx: C) -> Result<C> {
36 if self.index < self.middlewares.len() {
37 let mw = self.middlewares[self.index].clone();
38 let next = Next {
39 middlewares: self.middlewares.clone(),
40 index: self.index + 1,
41 terminal: self.terminal.take(),
42 };
43 mw.process(ctx, next).await
44 } else if let Some(term) = self.terminal.take() {
45 term(ctx).await
46 } else {
47 Ok(ctx)
48 }
49 }
50}
51
52pub struct MiddlewarePipeline<C: Send + 'static> {
54 middlewares: Arc<Vec<Arc<dyn Middleware<C>>>>,
55}
56
57impl<C: Send + 'static> Default for MiddlewarePipeline<C> {
58 fn default() -> Self {
59 Self {
60 middlewares: Arc::new(Vec::new()),
61 }
62 }
63}
64
65impl<C: Send + 'static> Clone for MiddlewarePipeline<C> {
66 fn clone(&self) -> Self {
67 Self {
68 middlewares: self.middlewares.clone(),
69 }
70 }
71}
72
73impl<C: Send + 'static> MiddlewarePipeline<C> {
74 pub fn new(middlewares: Vec<Arc<dyn Middleware<C>>>) -> Self {
75 Self {
76 middlewares: Arc::new(middlewares),
77 }
78 }
79
80 pub fn is_empty(&self) -> bool {
81 self.middlewares.is_empty()
82 }
83
84 pub async fn execute(&self, ctx: C, terminal: Terminal<C>) -> Result<C> {
86 let next = Next {
87 middlewares: self.middlewares.clone(),
88 index: 0,
89 terminal: Some(terminal),
90 };
91 next.run(ctx).await
92 }
93}
94
95pub struct AgentContext {
97 pub messages: Vec<Message>,
98 pub is_streaming: bool,
99 pub metadata: HashMap<String, serde_json::Value>,
100 pub result: Option<AgentResponse>,
102 pub terminate: bool,
104}
105
106impl AgentContext {
107 pub fn new(messages: Vec<Message>, is_streaming: bool) -> Self {
108 Self {
109 messages,
110 is_streaming,
111 metadata: HashMap::new(),
112 result: None,
113 terminate: false,
114 }
115 }
116}
117
118pub struct ChatContext {
120 pub messages: Vec<Message>,
121 pub chat_options: ChatOptions,
122 pub is_streaming: bool,
123 pub metadata: HashMap<String, serde_json::Value>,
124 pub result: Option<ChatResponse>,
125 pub terminate: bool,
126}
127
128impl ChatContext {
129 pub fn new(messages: Vec<Message>, chat_options: ChatOptions, is_streaming: bool) -> Self {
130 Self {
131 messages,
132 chat_options,
133 is_streaming,
134 metadata: HashMap::new(),
135 result: None,
136 terminate: false,
137 }
138 }
139}
140
141#[derive(Clone, Default)]
152pub struct LiveToolList {
153 inner: Arc<std::sync::Mutex<Vec<ToolDefinition>>>,
154}
155
156impl LiveToolList {
157 pub fn new(tools: Vec<ToolDefinition>) -> Self {
159 Self {
160 inner: Arc::new(std::sync::Mutex::new(tools)),
161 }
162 }
163
164 pub fn add_tools(&self, tools: impl IntoIterator<Item = ToolDefinition>) -> Result<()> {
170 let batch: Vec<ToolDefinition> = tools.into_iter().collect();
171 let mut list = self.inner.lock().unwrap();
172 for tool in &batch {
173 if list.iter().any(|t| t.name == tool.name)
174 || batch.iter().filter(|t| t.name == tool.name).count() > 1
175 {
176 return Err(Error::Configuration(format!(
177 "cannot add tool '{}': a tool with that name already exists in this run",
178 tool.name
179 )));
180 }
181 }
182 list.extend(batch);
183 Ok(())
184 }
185
186 pub fn remove_tools<'a>(&self, names: impl IntoIterator<Item = &'a str>) {
189 let to_remove: std::collections::HashSet<&str> = names.into_iter().collect();
190 self.inner
191 .lock()
192 .unwrap()
193 .retain(|t| !to_remove.contains(t.name.as_str()));
194 }
195
196 pub fn contains(&self, name: &str) -> bool {
198 self.inner.lock().unwrap().iter().any(|t| t.name == name)
199 }
200
201 pub fn snapshot(&self) -> Vec<ToolDefinition> {
203 self.inner.lock().unwrap().clone()
204 }
205}
206
207pub struct FunctionInvocationContext {
209 pub function_name: String,
210 pub arguments: serde_json::Value,
211 pub session: Option<crate::session::AgentSession>,
218 pub tools: Option<LiveToolList>,
225 pub metadata: HashMap<String, serde_json::Value>,
226 pub result: Option<serde_json::Value>,
227 pub terminate: bool,
228}
229
230impl FunctionInvocationContext {
231 pub fn new(function_name: impl Into<String>, arguments: serde_json::Value) -> Self {
232 Self {
233 function_name: function_name.into(),
234 arguments,
235 session: None,
236 tools: None,
237 metadata: HashMap::new(),
238 result: None,
239 terminate: false,
240 }
241 }
242
243 pub fn with_session(mut self, session: Option<crate::session::AgentSession>) -> Self {
245 self.session = session;
246 self
247 }
248
249 pub fn with_tools(mut self, tools: Option<LiveToolList>) -> Self {
251 self.tools = tools;
252 self
253 }
254
255 pub fn add_tools(&self, tools: impl IntoIterator<Item = ToolDefinition>) -> Result<()> {
259 self.tools
260 .as_ref()
261 .ok_or_else(|| {
262 Error::Configuration(
263 "cannot add tools: this FunctionInvocationContext is not bound to a \
264 live agent run"
265 .into(),
266 )
267 })?
268 .add_tools(tools)
269 }
270
271 pub fn remove_tools<'a>(&self, names: impl IntoIterator<Item = &'a str>) -> Result<()> {
275 self.tools
276 .as_ref()
277 .ok_or_else(|| {
278 Error::Configuration(
279 "cannot remove tools: this FunctionInvocationContext is not bound to a \
280 live agent run"
281 .into(),
282 )
283 })
284 .map(|t| t.remove_tools(names))
285 }
286}
287
288pub type AgentMiddleware = dyn Middleware<AgentContext>;
290pub type ChatMiddleware = dyn Middleware<ChatContext>;
292pub type FunctionMiddleware = dyn Middleware<FunctionInvocationContext>;
294
295pub struct FnMiddleware<C, F> {
297 f: F,
298 _marker: std::marker::PhantomData<fn(C)>,
299}
300
301impl<C, F> FnMiddleware<C, F> {
302 pub fn new(f: F) -> Self {
303 Self {
304 f,
305 _marker: std::marker::PhantomData,
306 }
307 }
308}
309
310#[async_trait]
311impl<C, F, Fut> Middleware<C> for FnMiddleware<C, F>
312where
313 C: Send + 'static,
314 F: Fn(C, Next<C>) -> Fut + Send + Sync,
315 Fut: std::future::Future<Output = Result<C>> + Send,
316{
317 async fn process(&self, ctx: C, next: Next<C>) -> Result<C> {
318 (self.f)(ctx, next).await
319 }
320}