1mod common;
2
3use common::run_cli_loop;
4use std::{collections::HashMap, sync::Arc};
5use tiny_loop::{Agent, llm::OpenAIProvider, tool::tool};
6use tokio::sync::Mutex;
7
8#[derive(Clone)]
9pub struct ReadonlyTool {
10 data: Arc<HashMap<String, String>>,
11}
12
13#[tool]
14impl ReadonlyTool {
15 pub async fn fetch(
17 self,
18 key: String,
20 ) -> String {
21 self.data
22 .get(&key)
23 .cloned()
24 .unwrap_or_else(|| format!("Key '{}' not found", key))
25 }
26}
27
28#[derive(Clone)]
29pub struct WritableTool {
30 data: Arc<Mutex<HashMap<String, String>>>,
31}
32
33#[tool]
34impl WritableTool {
35 pub async fn read(
37 self,
38 key: String,
40 ) -> String {
41 self.data
42 .lock()
43 .await
44 .get(&key)
45 .cloned()
46 .unwrap_or_default()
47 }
48
49 pub async fn write(
51 self,
52 key: String,
54 value: String,
56 ) -> String {
57 self.data.lock().await.insert(key.clone(), value.clone());
58 format!("Wrote '{}' to key '{}'", value, key)
59 }
60}
61
62#[tokio::main]
63async fn main() {
64 let api_key = std::env::var("LLM_API_KEY").expect("LLM_API_KEY not set");
65
66 let llm = OpenAIProvider::new()
67 .api_key(api_key)
68 .base_url("https://openrouter.ai/api/v1")
69 .model("google/gemini-3-flash-preview");
70
71 let mut data = HashMap::new();
72 data.insert("name".to_string(), "Alice".to_string());
73 data.insert("age".to_string(), "30".to_string());
74
75 let r = ReadonlyTool {
76 data: Arc::new(data),
77 };
78
79 let w = WritableTool {
80 data: Arc::new(Mutex::new(HashMap::new())),
81 };
82
83 let agent = Agent::new(llm)
84 .system("You are a helpful assistant with access to tools")
85 .bind(r.clone(), ReadonlyTool::fetch)
86 .bind(w.clone(), WritableTool::read)
87 .bind(w, WritableTool::write);
88
89 run_cli_loop(agent).await
90}