askit_std_agents/
file.rs

1use std::fs;
2use std::path::Path;
3
4use agent_stream_kit::{
5    ASKit, AgentConfigs, AgentContext, AgentError, AgentOutput, AgentValue, AsAgent, AgentData,
6    async_trait,
7};
8use askit_macros::askit_agent;
9
10static CATEGORY: &str = "Std/File";
11
12static PIN_PATH: &str = "path";
13static PIN_FILES: &str = "files";
14static PIN_TEXT: &str = "text";
15static PIN_DATA: &str = "data";
16
17// List Files Agent
18#[askit_agent(
19    title = "List Files",
20    category = CATEGORY,
21    inputs = [PIN_PATH],
22    outputs = [PIN_FILES]
23)]
24struct ListFilesAgent {
25    data: AgentData,
26}
27
28#[async_trait]
29impl AsAgent for ListFilesAgent {
30    fn new(
31        askit: ASKit,
32        id: String,
33        def_name: String,
34        config: Option<AgentConfigs>,
35    ) -> Result<Self, AgentError> {
36        Ok(Self {
37            data: AgentData::new(askit, id, def_name, config),
38        })
39    }
40
41    async fn process(
42        &mut self,
43        ctx: AgentContext,
44        _pin: String,
45        value: AgentValue,
46    ) -> Result<(), AgentError> {
47        let path = value
48            .as_str()
49            .ok_or_else(|| AgentError::InvalidValue("path is not a string".to_string()))?;
50        let path = Path::new(path);
51
52        if !path.exists() {
53            return Err(AgentError::InvalidValue(format!(
54                "Path does not exist: {}",
55                path.display()
56            )));
57        }
58
59        if !path.is_dir() {
60            return Err(AgentError::InvalidValue(format!(
61                "Path is not a directory: {}",
62                path.display()
63            )));
64        }
65
66        let mut files = Vec::new();
67        let entries = fs::read_dir(path).map_err(|e| {
68            AgentError::InvalidValue(format!(
69                "Failed to read directory {}: {}",
70                path.display(),
71                e
72            ))
73        })?;
74
75        for entry in entries {
76            let entry = entry.map_err(|e| {
77                AgentError::InvalidValue(format!("Failed to read directory entry: {}", e))
78            })?;
79            let file_name = entry.file_name().to_string_lossy().to_string();
80            files.push(file_name.into());
81        }
82
83        let out_value = AgentValue::array(files);
84        self.try_output(ctx, PIN_FILES, out_value)
85    }
86}
87
88// Read Text File Agent
89#[askit_agent(
90    title = "Read Text File",
91    category = CATEGORY,
92    inputs = [PIN_PATH],
93    outputs = [PIN_TEXT]
94)]
95struct ReadTextFileAgent {
96    data: AgentData,
97}
98
99#[async_trait]
100impl AsAgent for ReadTextFileAgent {
101    fn new(
102        askit: ASKit,
103        id: String,
104        def_name: String,
105        config: Option<AgentConfigs>,
106    ) -> Result<Self, AgentError> {
107        Ok(Self {
108            data: AgentData::new(askit, id, def_name, config),
109        })
110    }
111
112    async fn process(
113        &mut self,
114        ctx: AgentContext,
115        _pin: String,
116        value: AgentValue,
117    ) -> Result<(), AgentError> {
118        let path = value
119            .as_str()
120            .ok_or_else(|| AgentError::InvalidValue("path is not a string".into()))?;
121        let path = Path::new(path);
122
123        if !path.exists() {
124            return Err(AgentError::InvalidValue(format!(
125                "Path does not exist: {}",
126                path.display()
127            )));
128        }
129
130        if !path.is_file() {
131            return Err(AgentError::InvalidValue(format!(
132                "Path is not a file: {}",
133                path.display()
134            )));
135        }
136
137        let content = fs::read_to_string(path).map_err(|e| {
138            AgentError::InvalidValue(format!("Failed to read file {}: {}", path.display(), e))
139        })?;
140        let out_value = AgentValue::string(content);
141        self.try_output(ctx, PIN_TEXT, out_value)
142    }
143}
144
145// Write Text File Agent
146#[askit_agent(
147    title = "Write Text File",
148    category = CATEGORY,
149    inputs = [PIN_DATA],
150    outputs = [PIN_DATA]
151)]
152struct WriteTextFileAgent {
153    data: AgentData,
154}
155
156#[async_trait]
157impl AsAgent for WriteTextFileAgent {
158    fn new(
159        askit: ASKit,
160        id: String,
161        def_name: String,
162        config: Option<AgentConfigs>,
163    ) -> Result<Self, AgentError> {
164        Ok(Self {
165            data: AgentData::new(askit, id, def_name, config),
166        })
167    }
168
169    async fn process(
170        &mut self,
171        ctx: AgentContext,
172        _pin: String,
173        value: AgentValue,
174    ) -> Result<(), AgentError> {
175        let input = value
176            .as_object()
177            .ok_or_else(|| AgentError::InvalidValue("Input is not an object".into()))?;
178
179        let path = input
180            .get("path")
181            .ok_or_else(|| AgentError::InvalidValue("Missing 'path' in input".into()))?
182            .as_str()
183            .ok_or_else(|| AgentError::InvalidValue("'path' is not a string".into()))?;
184
185        let text = input
186            .get("text")
187            .ok_or_else(|| AgentError::InvalidValue("Missing 'text' in input".into()))?
188            .as_str()
189            .ok_or_else(|| AgentError::InvalidValue("'text' is not a string".into()))?;
190
191        let path = Path::new(path);
192
193        // Ensure parent directories exist
194        if let Some(parent) = path.parent() {
195            if !parent.exists() {
196                fs::create_dir_all(parent).map_err(|e| {
197                    AgentError::InvalidValue(format!("Failed to create parent directories: {}", e))
198                })?
199            }
200        }
201
202        fs::write(path, text).map_err(|e| {
203            AgentError::InvalidValue(format!("Failed to write file {}: {}", path.display(), e))
204        })?;
205
206        self.try_output(ctx, PIN_DATA, value)
207    }
208}