1use kiss_ai::{ContentBlock, ToolDef, Usage};
4use serde_json::Value;
5use std::sync::Arc;
6use tokio_util::sync::CancellationToken;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
9pub enum ExecutionMode {
10 Sequential,
11 #[default]
12 Parallel,
13}
14
15#[derive(Debug, Clone, Default)]
16pub struct ToolResult {
17 pub content: Vec<ContentBlock>,
19 pub details: Value,
21 pub is_error: bool,
23 pub usage: Option<Usage>,
25 pub terminate: bool,
27}
28
29impl ToolResult {
30 pub fn text(text: impl Into<String>) -> Self {
31 ToolResult {
32 content: vec![ContentBlock::text(text)],
33 ..Default::default()
34 }
35 }
36
37 pub fn output_text(&self) -> String {
38 self.content
39 .iter()
40 .filter_map(|c| match c {
41 ContentBlock::Text { text, .. } => Some(text.as_str()),
42 _ => None,
43 })
44 .collect::<Vec<_>>()
45 .join("\n")
46 }
47}
48
49pub type ToolUpdateSink = Arc<dyn Fn(ToolResult) + Send + Sync>;
51
52#[async_trait::async_trait]
53pub trait AgentTool: Send + Sync {
54 fn name(&self) -> &str;
55 fn label(&self) -> &str {
56 self.name()
57 }
58 fn description(&self) -> String;
59 fn parameters(&self) -> Value;
61 fn execution_mode(&self) -> ExecutionMode {
62 ExecutionMode::Parallel
63 }
64 fn prepare_arguments(&self, args: Value) -> Value {
66 args
67 }
68 async fn execute(
71 &self,
72 tool_call_id: &str,
73 args: Value,
74 cancel: CancellationToken,
75 on_update: Option<ToolUpdateSink>,
76 ) -> anyhow::Result<ToolResult>;
77
78 fn to_def(&self) -> ToolDef {
79 ToolDef {
80 name: self.name().to_string(),
81 description: self.description(),
82 parameters: self.parameters(),
83 }
84 }
85}
86
87pub type DynTool = Arc<dyn AgentTool>;