swiftide_agents/
state.rs

1//! Internal state of the agent
2
3use swiftide_core::chat_completion::ToolCall;
4
5#[derive(Clone, Debug, Default, strum_macros::EnumDiscriminants, strum_macros::EnumIs)]
6pub enum State {
7    #[default]
8    Pending,
9    Running,
10    Stopped(StopReason),
11}
12
13impl State {
14    pub fn stop_reason(&self) -> Option<&StopReason> {
15        match self {
16            State::Stopped(reason) => Some(reason),
17            _ => None,
18        }
19    }
20}
21
22/// The reason the agent stopped
23///
24/// `StopReason::Other` has some convenience methods to convert from any `AsRef<str>`
25#[non_exhaustive]
26#[derive(Clone, Debug, strum_macros::EnumIs, PartialEq)]
27pub enum StopReason {
28    /// A tool called stop
29    RequestedByTool(ToolCall),
30
31    /// A tool repeatedly failed
32    ToolCallsOverLimit(ToolCall),
33
34    /// A tool requires feedback before it will continue
35    FeedbackRequired {
36        tool_call: ToolCall,
37        payload: Option<serde_json::Value>,
38    },
39    /// There was an error
40    Error,
41
42    /// No new messages; stopping completions
43    NoNewMessages,
44
45    Other(String),
46}
47
48impl StopReason {
49    pub fn as_requested_by_tool(&self) -> Option<&ToolCall> {
50        if let StopReason::RequestedByTool(t) = self {
51            Some(t)
52        } else {
53            None
54        }
55    }
56
57    pub fn as_tool_calls_over_limit(&self) -> Option<&ToolCall> {
58        if let StopReason::ToolCallsOverLimit(t) = self {
59            Some(t)
60        } else {
61            None
62        }
63    }
64
65    pub fn as_feedback_required(&self) -> Option<(&ToolCall, Option<&serde_json::Value>)> {
66        if let StopReason::FeedbackRequired { tool_call, payload } = self {
67            Some((tool_call, payload.as_ref()))
68        } else {
69            None
70        }
71    }
72
73    pub fn as_error(&self) -> Option<()> {
74        if matches!(self, StopReason::Error) {
75            Some(())
76        } else {
77            None
78        }
79    }
80
81    pub fn as_no_new_messages(&self) -> Option<()> {
82        if matches!(self, StopReason::NoNewMessages) {
83            Some(())
84        } else {
85            None
86        }
87    }
88
89    pub fn as_other(&self) -> Option<&str> {
90        if let StopReason::Other(s) = self {
91            Some(s)
92        } else {
93            None
94        }
95    }
96}
97impl Default for StopReason {
98    fn default() -> Self {
99        StopReason::Other("No reason provided".to_string())
100    }
101}
102
103impl<S: AsRef<str>> From<S> for StopReason {
104    fn from(value: S) -> Self {
105        StopReason::Other(value.as_ref().to_string())
106    }
107}