02_with_tools/
02_with_tools.rs1use ceylon_next::agent::Agent;
9use ceylon_next::tasks::{OutputData, TaskRequest};
10use ceylon_next::tools::ToolTrait;
11use serde_json::{json, Value};
12
13struct CalculatorTool;
15
16impl ToolTrait for CalculatorTool {
17 fn name(&self) -> String {
18 "calculator".to_string()
19 }
20
21 fn description(&self) -> String {
22 "A calculator tool that can perform basic mathematical operations. \
23 Accepts 'operation' (add, subtract, multiply, divide) and 'numbers' (array of numbers)."
24 .to_string()
25 }
26
27 fn input_schema(&self) -> Value {
28 json!({
29 "type": "object",
30 "properties": {
31 "operation": {
32 "type": "string",
33 "description": "The operation to perform: add, subtract, multiply, divide"
34 },
35 "numbers": {
36 "type": "array",
37 "items": { "type": "number" },
38 "description": "Array of numbers to perform the operation on"
39 }
40 },
41 "required": ["operation", "numbers"]
42 })
43 }
44
45 fn execute(&self, input: Value) -> Value {
46 let operation = input["operation"].as_str().unwrap_or("add");
47 let numbers: Vec<f64> = input["numbers"]
48 .as_array()
49 .unwrap_or(&vec![])
50 .iter()
51 .filter_map(|v| v.as_f64())
52 .collect();
53
54 let result = match operation {
55 "add" => numbers.iter().sum(),
56 "subtract" => {
57 numbers.iter().enumerate().fold(0.0, |acc, (i, &num)| {
58 if i == 0 {
59 num
60 } else {
61 acc - num
62 }
63 })
64 }
65 "multiply" => numbers.iter().product(),
66 "divide" => {
67 numbers.iter().enumerate().fold(1.0, |acc, (i, &num)| {
68 if i == 0 {
69 num
70 } else {
71 acc / num
72 }
73 })
74 }
75 _ => 0.0,
76 };
77
78 json!({
79 "operation": operation,
80 "numbers": numbers,
81 "result": result
82 })
83 }
84}
85
86struct StringToolTool;
88
89impl ToolTrait for StringToolTool {
90 fn name(&self) -> String {
91 "string_tool".to_string()
92 }
93
94 fn description(&self) -> String {
95 "A string manipulation tool that can uppercase, lowercase, or reverse strings."
96 .to_string()
97 }
98
99 fn input_schema(&self) -> Value {
100 json!({
101 "type": "object",
102 "properties": {
103 "operation": {
104 "type": "string",
105 "description": "The operation to perform: uppercase, lowercase, reverse, length"
106 },
107 "text": {
108 "type": "string",
109 "description": "The text to manipulate"
110 }
111 },
112 "required": ["operation", "text"]
113 })
114 }
115
116 fn execute(&self, input: Value) -> Value {
117 let operation = input["operation"].as_str().unwrap_or("uppercase");
118 let text = input["text"].as_str().unwrap_or("");
119
120 let result = match operation {
121 "uppercase" => text.to_uppercase(),
122 "lowercase" => text.to_lowercase(),
123 "reverse" => text.chars().rev().collect(),
124 "length" => text.len().to_string(),
125 _ => "Unknown operation".to_string(),
126 };
127
128 json!({
129 "operation": operation,
130 "input": text,
131 "result": result
132 })
133 }
134}
135
136#[tokio::main]
137async fn main() {
138 println!("🤖 Ceylon Agent - Tools Example\n");
139
140 let mut agent = Agent::new("MathAssistant", "ollama::gemma3:latest");
142
143 agent.with_system_prompt(
144 "You are a helpful math and text assistant. \
145 Use the available tools to help the user. \
146 Be clear about what tools you're using and why."
147 );
148
149 println!("🔧 Registering tools...");
151 agent.add_tool(CalculatorTool);
152 agent.add_tool(StringToolTool);
153 println!("✓ Tools registered\n");
154
155 let mut task = TaskRequest::new(
157 "Please calculate 25 + 15 + 10, and then tell me the uppercase version of the word 'hello'"
158 );
159 task.with_name("Complex Task")
160 .with_description("A task requiring multiple tools")
161 .with_priority(8);
162
163 println!("📋 Task: {}\n", task.id());
165 let response = agent.run(task).await;
166
167 match response.result() {
169 OutputData::Text(answer) => {
170 println!("📝 Agent Response:\n{}\n", answer);
171 }
172 _ => {
173 println!("❌ Unexpected response type");
174 }
175 }
176
177 println!("✅ Example completed successfully!");
178}