swiftide_agents/tools/
control.rs

1//! Control tools manage control flow during agent's lifecycle.
2use anyhow::Result;
3use async_trait::async_trait;
4use std::borrow::Cow;
5use swiftide_core::{
6    AgentContext, ToolFeedback,
7    chat_completion::{Tool, ToolCall, ToolOutput, ToolSpec, errors::ToolError},
8};
9
10/// `Stop` tool is a default tool used by agents to stop
11#[derive(Clone, Debug, Default)]
12pub struct Stop {}
13
14#[async_trait]
15impl Tool for Stop {
16    async fn invoke(
17        &self,
18        _agent_context: &dyn AgentContext,
19        _tool_call: &ToolCall,
20    ) -> Result<ToolOutput, ToolError> {
21        Ok(ToolOutput::Stop)
22    }
23
24    fn name(&self) -> Cow<'_, str> {
25        "stop".into()
26    }
27
28    fn tool_spec(&self) -> ToolSpec {
29        ToolSpec::builder()
30            .name("stop")
31            .description("When you have completed, or cannot complete, your task, call this")
32            .build()
33            .unwrap()
34    }
35}
36
37impl From<Stop> for Box<dyn Tool> {
38    fn from(val: Stop) -> Self {
39        Box::new(val)
40    }
41}
42
43#[derive(Clone)]
44/// Wraps a tool and requires approval before it can be used
45pub struct ApprovalRequired(pub Box<dyn Tool>);
46
47impl ApprovalRequired {
48    /// Creates a new `ApprovalRequired` tool
49    pub fn new(tool: impl Tool + 'static) -> Self {
50        Self(Box::new(tool))
51    }
52}
53
54#[async_trait]
55impl Tool for ApprovalRequired {
56    async fn invoke(
57        &self,
58        context: &dyn AgentContext,
59        tool_call: &ToolCall,
60    ) -> Result<ToolOutput, ToolError> {
61        if let Some(feedback) = context.has_received_feedback(tool_call).await {
62            match feedback {
63                ToolFeedback::Approved { .. } => return self.0.invoke(context, tool_call).await,
64                ToolFeedback::Refused { .. } => {
65                    return Ok(ToolOutput::text("This tool call was refused"));
66                }
67            }
68        }
69
70        Ok(ToolOutput::FeedbackRequired(None))
71    }
72
73    fn name(&self) -> Cow<'_, str> {
74        self.0.name()
75    }
76
77    fn tool_spec(&self) -> ToolSpec {
78        self.0.tool_spec()
79    }
80}
81
82impl From<ApprovalRequired> for Box<dyn Tool> {
83    fn from(val: ApprovalRequired) -> Self {
84        Box::new(val)
85    }
86}