1use std::collections::HashMap;
19use std::io::{self, BufRead, Write};
20use std::sync::Arc;
21
22use adk_core::{Agent, Content, Memory, Part, Result, SessionId, UserId};
23use adk_session::{CreateRequest, InMemorySessionService, SessionService};
24use futures::StreamExt;
25
26use crate::{Runner, RunnerConfig};
27
28pub struct Launcher {
37 agent: Arc<dyn Agent>,
38 app_name: Option<String>,
39 session_service: Option<Arc<dyn SessionService>>,
40 memory_service: Option<Arc<dyn Memory>>,
41}
42
43impl Launcher {
44 pub fn new(agent: Arc<dyn Agent>) -> Self {
46 Self { agent, app_name: None, session_service: None, memory_service: None }
47 }
48
49 pub fn app_name(mut self, name: impl Into<String>) -> Self {
51 self.app_name = Some(name.into());
52 self
53 }
54
55 pub fn with_session_service(mut self, service: Arc<dyn SessionService>) -> Self {
57 self.session_service = Some(service);
58 self
59 }
60
61 pub fn with_memory_service(mut self, service: Arc<dyn Memory>) -> Self {
63 self.memory_service = Some(service);
64 self
65 }
66
67 pub async fn run(self) -> Result<()> {
72 let app_name = self.app_name.unwrap_or_else(|| self.agent.name().to_string());
73 let agent = self.agent;
74
75 let session_service: Arc<dyn SessionService> =
76 self.session_service.unwrap_or_else(|| Arc::new(InMemorySessionService::new()));
77
78 let session = session_service
79 .create(CreateRequest {
80 app_name: app_name.clone(),
81 user_id: "user".into(),
82 session_id: None,
83 state: HashMap::new(),
84 })
85 .await?;
86
87 let session_id = session.id().to_string();
88
89 println!();
90 println!(" ADK-Rust — {}", agent.name());
91 println!(" Type a message to chat. Type 'exit' to quit.");
92 println!();
93
94 let stdin = io::stdin();
95 let mut lines = stdin.lock().lines();
96
97 loop {
98 print!("You > ");
99 io::stdout().flush().ok();
100
101 let line = match lines.next() {
102 Some(Ok(line)) => line,
103 Some(Err(_)) | None => break,
104 };
105
106 let trimmed = line.trim();
107 if trimmed.is_empty() {
108 continue;
109 }
110 if matches!(trimmed, "exit" | "quit" | "/exit" | "/quit") {
111 println!("\nGoodbye.\n");
112 break;
113 }
114
115 let user_content = Content::new("user").with_text(trimmed);
116
117 let runner = Runner::new(RunnerConfig {
118 app_name: app_name.clone(),
119 agent: agent.clone(),
120 session_service: session_service.clone(),
121 #[cfg(feature = "artifacts")]
122 artifact_service: None,
123 memory_service: self.memory_service.clone(),
124 #[cfg(feature = "plugins")]
125 plugin_manager: None,
126 run_config: None,
127 compaction_config: None,
128 context_cache_config: None,
129 cache_capable: None,
130 request_context: None,
131 cancellation_token: None,
132 intra_compaction_config: None,
133 intra_compaction_summarizer: None,
134 })?;
135
136 let mut stream = runner
137 .run(UserId::new("user")?, SessionId::new(&session_id)?, user_content)
138 .await?;
139
140 while let Some(event) = stream.next().await {
141 match event {
142 Ok(evt) => {
143 if let Some(content) = evt.content() {
144 for part in &content.parts {
145 match part {
146 Part::Text { text } => {
147 print!("{text}");
148 io::stdout().flush().ok();
149 }
150 Part::Thinking { thinking, .. } => {
151 print!("\n[thinking] {thinking}");
152 io::stdout().flush().ok();
153 }
154 Part::FunctionCall { name, args, .. } => {
155 print!("\n[tool] {name}({args})");
156 io::stdout().flush().ok();
157 }
158 _ => {}
159 }
160 }
161 }
162 }
163 Err(e) => {
164 eprintln!("\n[error] {e}");
165 }
166 }
167 }
168
169 println!("\n");
170 }
171
172 Ok(())
173 }
174}