1use rpi_ai::types::{AssistantMessage, ImageContent, TextContent, ToolResultMessage, Usage};
10use std::collections::HashSet;
11use std::sync::Arc;
12
13use crate::message::AgentMessage;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
22pub enum ToolExecutionMode {
23 Sequential,
24 #[default]
25 Parallel,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
31pub enum QueueMode {
32 All,
33 #[default]
34 OneAtATime,
35}
36
37#[derive(Debug, Clone, Default)]
41pub struct AgentToolResult {
42 pub content: Vec<TextContentOrImage>,
43 pub details: serde_json::Value,
44 pub usage: Option<Usage>,
45 pub added_tool_names: Vec<String>,
46 pub terminate: bool,
47}
48#[derive(Debug, Clone, PartialEq, Eq)]
51pub enum TextContentOrImage {
52 Text(TextContent),
53 Image(ImageContent),
54}
55
56impl TextContentOrImage {
57 pub fn text<S: Into<String>>(s: S) -> Self {
58 TextContentOrImage::Text(TextContent {
59 kind: rpi_ai::types::TextContentType,
60 text: s.into(),
61 text_signature: None,
62 })
63 }
64}
65
66impl AgentToolResult {
67 pub fn text(message: impl Into<String>) -> Self {
69 Self {
70 content: vec![TextContentOrImage::text(message)],
71 details: serde_json::Value::Null,
72 usage: None,
73 added_tool_names: Vec::new(),
74 terminate: false,
75 }
76 }
77
78 pub fn error_text(message: impl Into<String>) -> Self {
81 Self::text(message)
82 }
83
84 pub fn into_content(self) -> Vec<rpi_ai::types::Content> {
85 self.content
86 .into_iter()
87 .map(|c| match c {
88 TextContentOrImage::Text(t) => rpi_ai::types::Content::Text(t),
89 TextContentOrImage::Image(i) => rpi_ai::types::Content::Image(i),
90 })
91 .collect()
92 }
93}
94
95impl From<AgentToolResult> for Result<AgentToolResult, crate::AgentError> {
96 fn from(r: AgentToolResult) -> Self {
97 Ok(r)
98 }
99}
100
101pub type ToolResultPartial = AgentToolResult;
104
105#[derive(Debug, Clone, Default)]
117pub struct BeforeToolCallResult {
118 pub block: bool,
119 pub reason: Option<String>,
120 pub terminate: bool,
121 pub args: Option<serde_json::Value>,
122}
123
124#[derive(Debug, Clone, Default)]
128pub struct AfterToolCallResult {
129 pub content: Option<Vec<TextContentOrImage>>,
130 pub details: Option<serde_json::Value>,
131 pub is_error: Option<bool>,
132 pub usage: Option<Usage>,
133 pub terminate: Option<bool>,
134}
135
136pub struct BeforeToolCallContext<'a> {
138 pub assistant_message: &'a AssistantMessage,
139 pub tool_call: &'a rpi_ai::types::ToolCall,
140 pub args: &'a serde_json::Value,
141 pub context: &'a AgentContext,
142}
143
144pub struct AfterToolCallContext<'a> {
146 pub assistant_message: &'a AssistantMessage,
147 pub tool_call: &'a rpi_ai::types::ToolCall,
148 pub args: &'a serde_json::Value,
149 pub result: &'a AgentToolResult,
150 pub is_error: bool,
151 pub context: &'a AgentContext,
152}
153
154pub struct ShouldStopAfterTurnContext<'a> {
156 pub message: &'a AssistantMessage,
157 pub tool_results: &'a [ToolResultMessage],
158 pub context: &'a AgentContext,
159 pub new_messages: &'a [AgentMessage],
160}
161
162#[derive(Clone, Default)]
164pub struct AgentContext {
165 pub system_prompt: String,
166 pub messages: Vec<AgentMessage>,
167 pub tools: Vec<Arc<dyn crate::agent_tool::AgentTool>>,
168}
169
170impl std::fmt::Debug for AgentContext {
171 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172 f.debug_struct("AgentContext")
173 .field("system_prompt", &self.system_prompt)
174 .field("messages", &self.messages)
175 .field(
176 "tools",
177 &self
178 .tools
179 .iter()
180 .map(|t| t.schema().name.as_str())
181 .collect::<Vec<_>>(),
182 )
183 .finish()
184 }
185}
186
187impl AgentContext {
188 pub fn new(messages: Vec<AgentMessage>) -> Self {
189 Self {
190 system_prompt: String::new(),
191 messages,
192 tools: Vec::new(),
193 }
194 }
195}
196
197#[derive(Debug, Clone, Default)]
200pub struct AgentLoopTurnUpdate {
201 pub context: Option<AgentContext>,
202 pub model: Option<rpi_ai::model::Model>,
203 pub thinking_level: Option<rpi_ai::types::ThinkingLevel>,
204}
205
206#[derive(Clone)]
209pub struct AgentState {
210 pub system_prompt: String,
211 pub model: rpi_ai::model::Model,
212 pub thinking_level: rpi_ai::types::ThinkingLevel,
213 pub tools: Vec<Arc<dyn crate::agent_tool::AgentTool>>,
214 pub messages: Vec<AgentMessage>,
215 pub is_streaming: bool,
216 pub streaming_message: Option<AgentMessage>,
217 pub pending_tool_calls: HashSet<String>,
218 pub error_message: Option<String>,
219}
220
221impl std::fmt::Debug for AgentState {
222 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
223 f.debug_struct("AgentState")
224 .field("system_prompt", &self.system_prompt)
225 .field("model", &self.model)
226 .field("thinking_level", &self.thinking_level)
227 .field(
228 "tools",
229 &self
230 .tools
231 .iter()
232 .map(|t| t.schema().name.as_str())
233 .collect::<Vec<_>>(),
234 )
235 .field("messages", &self.messages)
236 .field("is_streaming", &self.is_streaming)
237 .field("streaming_message", &self.streaming_message)
238 .field("pending_tool_calls", &self.pending_tool_calls)
239 .field("error_message", &self.error_message)
240 .finish()
241 }
242}
243
244impl Default for AgentState {
245 fn default() -> Self {
246 Self {
247 system_prompt: String::new(),
248 model: default_model(),
249 thinking_level: rpi_ai::types::ThinkingLevel::Off,
250 tools: Vec::new(),
251 messages: Vec::new(),
252 is_streaming: false,
253 streaming_message: None,
254 pending_tool_calls: HashSet::new(),
255 error_message: None,
256 }
257 }
258}
259
260pub(crate) fn default_model() -> rpi_ai::model::Model {
262 rpi_ai::model::Model::new(
263 "unknown",
264 "unknown",
265 rpi_ai::types::Api::Other("unknown".into()),
266 "unknown",
267 "",
268 )
269}