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("tools", &self.tools.iter().map(|t| t.schema().name.as_str()).collect::<Vec<_>>())
176 .finish()
177 }
178}
179
180impl AgentContext {
181 pub fn new(messages: Vec<AgentMessage>) -> Self {
182 Self {
183 system_prompt: String::new(),
184 messages,
185 tools: Vec::new(),
186 }
187 }
188}
189
190#[derive(Debug, Clone, Default)]
193pub struct AgentLoopTurnUpdate {
194 pub context: Option<AgentContext>,
195 pub model: Option<rpi_ai::model::Model>,
196 pub thinking_level: Option<rpi_ai::types::ThinkingLevel>,
197}
198
199#[derive(Clone)]
202pub struct AgentState {
203 pub system_prompt: String,
204 pub model: rpi_ai::model::Model,
205 pub thinking_level: rpi_ai::types::ThinkingLevel,
206 pub tools: Vec<Arc<dyn crate::agent_tool::AgentTool>>,
207 pub messages: Vec<AgentMessage>,
208 pub is_streaming: bool,
209 pub streaming_message: Option<AgentMessage>,
210 pub pending_tool_calls: HashSet<String>,
211 pub error_message: Option<String>,
212}
213
214impl std::fmt::Debug for AgentState {
215 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216 f.debug_struct("AgentState")
217 .field("system_prompt", &self.system_prompt)
218 .field("model", &self.model)
219 .field("thinking_level", &self.thinking_level)
220 .field("tools", &self.tools.iter().map(|t| t.schema().name.as_str()).collect::<Vec<_>>())
221 .field("messages", &self.messages)
222 .field("is_streaming", &self.is_streaming)
223 .field("streaming_message", &self.streaming_message)
224 .field("pending_tool_calls", &self.pending_tool_calls)
225 .field("error_message", &self.error_message)
226 .finish()
227 }
228}
229
230impl Default for AgentState {
231 fn default() -> Self {
232 Self {
233 system_prompt: String::new(),
234 model: default_model(),
235 thinking_level: rpi_ai::types::ThinkingLevel::Off,
236 tools: Vec::new(),
237 messages: Vec::new(),
238 is_streaming: false,
239 streaming_message: None,
240 pending_tool_calls: HashSet::new(),
241 error_message: None,
242 }
243 }
244}
245
246pub(crate) fn default_model() -> rpi_ai::model::Model {
248 rpi_ai::model::Model::new("unknown", "unknown", rpi_ai::types::Api::Other("unknown".into()), "unknown", "")
249}