10_file_saving/
10_file_saving.rs1use ceylon_next::agent::Agent;
10use ceylon_next::tasks::{OutputData, TaskRequest};
11use ceylon_next::tools::ToolTrait;
12use serde_json::{json, Value};
13use std::fs;
14use std::path::Path;
15
16struct FileSaverTool;
18
19impl ToolTrait for FileSaverTool {
20 fn name(&self) -> String {
21 "file_saver".to_string()
22 }
23
24 fn description(&self) -> String {
25 "A tool that saves content to a file. \
26 Accepts 'filename' (path to save the file) and 'content' (text content to save). \
27 Returns success status and the file path."
28 .to_string()
29 }
30
31 fn input_schema(&self) -> Value {
32 json!({
33 "type": "object",
34 "properties": {
35 "filename": {
36 "type": "string",
37 "description": "The path where the file should be saved (e.g., 'output.txt', 'data/results.json')"
38 },
39 "content": {
40 "type": "string",
41 "description": "The text content to write to the file"
42 }
43 },
44 "required": ["filename", "content"]
45 })
46 }
47
48 fn execute(&self, input: Value) -> Value {
49 let filename = input["filename"].as_str().unwrap_or("output.txt");
50 let content = input["content"].as_str().unwrap_or("");
51
52 if let Some(parent) = Path::new(filename).parent() {
54 if !parent.as_os_str().is_empty() {
55 if let Err(e) = fs::create_dir_all(parent) {
56 return json!({
57 "success": false,
58 "filename": filename,
59 "error": format!("Failed to create directory: {}", e)
60 });
61 }
62 }
63 }
64
65 match fs::write(filename, content) {
67 Ok(_) => json!({
68 "success": true,
69 "filename": filename,
70 "bytes_written": content.len(),
71 "message": format!("Successfully saved {} bytes to {}", content.len(), filename)
72 }),
73 Err(e) => json!({
74 "success": false,
75 "filename": filename,
76 "error": format!("Failed to write file: {}", e)
77 }),
78 }
79 }
80}
81
82#[tokio::main]
83async fn main() {
84 println!("š¤ Ceylon Agent - File Saving Example\n");
85
86 let output_dir = "test/web_project_output";
88
89 if let Err(e) = fs::create_dir_all(output_dir) {
91 eprintln!("ā Failed to create output directory: {}", e);
92 return;
93 }
94 println!("š Output directory: {}/\n", output_dir);
95
96 let mut agent = Agent::new("ContentGenerator", "ollama::gemma3:latest");
98
99 agent.with_system_prompt(
100 &format!(
101 "You are a helpful assistant that can generate content and save it to files. \
102 When asked to create content, generate it and use the file_saver tool to save it. \
103 \
104 IMPORTANT: All files MUST be saved inside the '{}' directory. \
105 For example, to save 'server.js', use '{}/server.js' as the filename. \
106 For files in subdirectories, use '{}/src/app.js' format. \
107 \
108 Be clear about what you're creating and where you're saving it.",
109 output_dir, output_dir, output_dir
110 )
111 );
112
113 println!("š§ Registering file saver tool...");
115 agent.add_tool(FileSaverTool);
116 println!("ā Tool registered\n");
117
118 let mut task = TaskRequest::new(
120 &format!(
121 "Create a simple web project with a file upload API. Save all files in the '{}' directory:\
122 \n1. server.js - A Node.js Express server with POST /upload endpoint\
123 \n2. package.json - Package configuration with dependencies\
124 \n3. test.http - HTTP test file to test the upload endpoint\
125 \n4. README.md - Instructions on how to install and run the server\
126 \n\nMake sure ALL files are saved in the '{}' directory!",
127 output_dir, output_dir
128 )
129 );
130 task.with_name("Generate and Save Web Project")
131 .with_description("Generate a complete web project with file upload API")
132 .with_priority(7);
133
134 println!("š Task: {}\n", task.id());
136 println!("š Running agent...\n");
137
138 let response = agent.run(task).await;
139
140 match response.result() {
142 OutputData::Text(answer) => {
143 println!("\nš Agent Response:\n{}\n", answer);
144 }
145 _ => {
146 println!("ā Unexpected response type");
147 }
148 }
149
150 println!("š Checking created files in '{}/'\n", output_dir);
152
153 let expected_files = vec![
154 "server.js",
155 "package.json",
156 "test.http",
157 "README.md"
158 ];
159
160 let mut created_count = 0;
161
162 for file in &expected_files {
163 let file_path = format!("{}/{}", output_dir, file);
164 if Path::new(&file_path).exists() {
165 created_count += 1;
166 println!("ā {} created successfully", file);
167
168 if let Ok(content) = fs::read_to_string(&file_path) {
169 let preview: String = content.chars().take(100).collect();
170 println!(" Preview: {}", preview.replace('\n', " "));
171 }
172 } else {
173 println!("ā {} not found", file);
174 }
175 }
176
177 println!("\nš Summary: {}/{} expected files created", created_count, expected_files.len());
178
179 println!("\nš Directory structure:");
181 if let Ok(entries) = fs::read_dir(output_dir) {
182 for entry in entries.flatten() {
183 let path = entry.path();
184 if path.is_file() {
185 if let Some(filename) = path.file_name() {
186 if let Ok(metadata) = entry.metadata() {
187 println!(" - {} ({} bytes)",
188 filename.to_string_lossy(),
189 metadata.len());
190 }
191 }
192 }
193 }
194 }
195
196 println!("\nā
Example completed successfully!");
197 println!("š All files are in: {}/", output_dir);
198}