gemini_adk_fluent_rs/live/config.rs
1//! Model, session, and tool configuration methods for `Live`.
2
3use std::sync::Arc;
4use std::time::Duration;
5
6use gemini_adk_rs::live::needs::RepairConfig;
7use gemini_adk_rs::live::persistence::SessionPersistence;
8use gemini_adk_rs::live::steering::{ContextDelivery, SteeringMode};
9use gemini_adk_rs::live::{ResultFormatter, ToolExecutionMode};
10use gemini_adk_rs::tool::ToolDispatcher;
11use gemini_genai_rs::prelude::*;
12
13use super::{DeferredAgentTool, Live};
14
15impl Live {
16 // -- Model & Voice --
17
18 /// Set the Gemini model.
19 pub fn model(mut self, model: GeminiModel) -> Self {
20 self.config = self.config.model(model);
21 self
22 }
23
24 /// Set the output voice.
25 pub fn voice(mut self, voice: Voice) -> Self {
26 self.config = self.config.voice(voice);
27 self
28 }
29
30 /// Set the system instruction.
31 pub fn instruction(mut self, instruction: impl Into<String>) -> Self {
32 self.config = self.config.system_instruction(instruction);
33 self
34 }
35
36 /// Switch to text-only mode (no audio output).
37 ///
38 /// Sets response modality to `Text` and disables speech config.
39 /// Use with `GeminiModel::Gemini2_0FlashLive` for text-only conversations.
40 pub fn text_only(mut self) -> Self {
41 self.config = self.config.text_only();
42 self
43 }
44
45 /// Add a raw `Tool` declaration to the session configuration.
46 ///
47 /// Use this for tools that aren't registered through the `ToolDispatcher`
48 /// (e.g., raw `FunctionDeclaration` lists, Google Search, code execution).
49 pub fn add_tool(mut self, tool: Tool) -> Self {
50 self.config = self.config.add_tool(tool);
51 self
52 }
53
54 /// Set a greeting prompt to trigger the model to initiate the conversation.
55 ///
56 /// When set, this text is sent immediately after the session connects,
57 /// causing the model to respond first (e.g. with a greeting or introduction).
58 ///
59 /// ```ignore
60 /// let handle = Live::builder()
61 /// .model(GeminiModel::Gemini2_0FlashLive)
62 /// .instruction("You are a friendly assistant")
63 /// .greeting("Greet the user warmly and introduce yourself.")
64 /// .connect_vertex(project, location, token)
65 /// .await?;
66 /// // Model will speak first without any user input
67 /// ```
68 pub fn greeting(mut self, prompt: impl Into<String>) -> Self {
69 self.greeting = Some(prompt.into());
70 self
71 }
72
73 /// Set the temperature.
74 pub fn temperature(mut self, temp: f32) -> Self {
75 self.config = self.config.temperature(temp);
76 self
77 }
78
79 // -- Tools --
80
81 /// Set the tool dispatcher (auto-dispatches tool calls).
82 pub fn tools(mut self, dispatcher: ToolDispatcher) -> Self {
83 self.dispatcher = Some(dispatcher);
84 self
85 }
86
87 /// Register tools from a `T` module composition.
88 ///
89 /// ```ignore
90 /// use gemini_adk_fluent_rs::prelude::*;
91 ///
92 /// Live::builder()
93 /// .with_tools(
94 /// T::simple("get_weather", "Get weather", |args| async move {
95 /// Ok(serde_json::json!({"temp": 22}))
96 /// })
97 /// | T::google_search()
98 /// )
99 /// ```
100 pub fn with_tools(mut self, composite: crate::compose::tools::ToolComposite) -> Self {
101 let dispatcher = self.dispatcher.get_or_insert_with(ToolDispatcher::new);
102 for entry in composite.entries {
103 match entry {
104 crate::compose::tools::ToolCompositeEntry::Function(f) => {
105 dispatcher.register_function(f);
106 }
107 crate::compose::tools::ToolCompositeEntry::BuiltIn(tool) => {
108 // Built-in tools go directly to session config
109 self.config = self.config.add_tool(tool);
110 }
111 // Placeholder variants — declarative markers resolved at runtime.
112 _ => {
113 // Agent, Mcp, A2a, Mock, OpenApi, Search, Schema, Transform
114 // are stored for later resolution by the runtime layer.
115 }
116 }
117 }
118 self
119 }
120
121 /// Register a text agent as a tool the live model can call.
122 ///
123 /// The agent shares the session's `State`, so it can read live-extracted
124 /// values and its mutations are visible to watchers and phase transitions.
125 ///
126 /// ```ignore
127 /// Live::builder()
128 /// .agent_tool("verify_identity", "Verify caller identity", verifier_agent)
129 /// .agent_tool("calc_payment", "Calculate payment plans", calc_pipeline)
130 /// ```
131 pub fn agent_tool(
132 mut self,
133 name: impl Into<String>,
134 description: impl Into<String>,
135 agent: impl gemini_adk_rs::text::TextAgent + 'static,
136 ) -> Self {
137 self.deferred_agent_tools.push(DeferredAgentTool {
138 name: name.into(),
139 description: description.into(),
140 agent: Arc::new(agent),
141 });
142 self
143 }
144
145 /// Register a text agent (already `Arc`'d) as a tool.
146 pub fn agent_tool_arc(
147 mut self,
148 name: impl Into<String>,
149 description: impl Into<String>,
150 agent: Arc<dyn gemini_adk_rs::text::TextAgent>,
151 ) -> Self {
152 self.deferred_agent_tools.push(DeferredAgentTool {
153 name: name.into(),
154 description: description.into(),
155 agent,
156 });
157 self
158 }
159
160 /// Enable Google Search built-in tool.
161 pub fn google_search(mut self) -> Self {
162 self.config = self.config.with_google_search();
163 self
164 }
165
166 /// Enable code execution built-in tool.
167 pub fn code_execution(mut self) -> Self {
168 self.config = self.config.with_code_execution();
169 self
170 }
171
172 /// Enable URL context built-in tool.
173 pub fn url_context(mut self) -> Self {
174 self.config = self.config.with_url_context();
175 self
176 }
177
178 /// Mark a tool for background execution (zero dead-air).
179 ///
180 /// When the model calls this tool, an immediate "running" acknowledgment
181 /// is sent back while the tool executes in a background task. The final
182 /// result is delivered asynchronously when complete.
183 pub fn tool_background(mut self, tool_name: impl Into<String>) -> Self {
184 self.tool_execution_modes.insert(
185 tool_name.into(),
186 ToolExecutionMode::Background {
187 formatter: None,
188 scheduling: None,
189 },
190 );
191 self
192 }
193
194 /// Mark a tool for background execution with a custom result formatter.
195 ///
196 /// The formatter controls the shape of the acknowledgment ("running"),
197 /// completion, and cancellation messages sent to the model.
198 pub fn tool_background_with_formatter(
199 mut self,
200 tool_name: impl Into<String>,
201 formatter: Arc<dyn ResultFormatter>,
202 ) -> Self {
203 self.tool_execution_modes.insert(
204 tool_name.into(),
205 ToolExecutionMode::Background {
206 formatter: Some(formatter),
207 scheduling: None,
208 },
209 );
210 self
211 }
212
213 /// Mark a tool for background execution with a specific scheduling mode.
214 ///
215 /// The scheduling mode controls how the model handles async results:
216 /// - `Interrupt`: halts current output, immediately reports the result
217 /// - `WhenIdle`: waits until current output finishes before handling
218 /// - `Silent`: integrates the result without notifying the user
219 pub fn tool_background_with_scheduling(
220 mut self,
221 tool_name: impl Into<String>,
222 scheduling: gemini_genai_rs::prelude::FunctionResponseScheduling,
223 ) -> Self {
224 self.tool_execution_modes.insert(
225 tool_name.into(),
226 ToolExecutionMode::Background {
227 formatter: None,
228 scheduling: Some(scheduling),
229 },
230 );
231 self
232 }
233
234 // -- Audio/Video Config --
235
236 /// Enable input and/or output transcription.
237 pub fn transcription(mut self, input: bool, output: bool) -> Self {
238 if input {
239 self.config = self.config.enable_input_transcription();
240 }
241 if output {
242 self.config = self.config.enable_output_transcription();
243 }
244 self
245 }
246
247 /// Enable thinking/reasoning with a token budget (Gemini 2.5+).
248 ///
249 /// Sets the thinking budget for the Live session. Use with
250 /// `.include_thoughts()` and `.on_thought()` to receive thought summaries.
251 ///
252 /// ```ignore
253 /// Live::builder()
254 /// .thinking(1024)
255 /// .include_thoughts()
256 /// .on_thought(|text| println!("[Thought] {text}"))
257 /// ```
258 ///
259 /// **Platform support:** Google AI only. On Vertex AI, `thinkingConfig`
260 /// is automatically stripped from the setup message.
261 pub fn thinking(mut self, budget: u32) -> Self {
262 self.config = self.config.thinking(budget);
263 self
264 }
265
266 /// Include the model's thought summaries in responses.
267 ///
268 /// When enabled, the model emits `SessionEvent::Thought` events containing
269 /// its reasoning process. Register an `.on_thought()` callback to receive them.
270 ///
271 /// **Platform support:** Google AI only. Stripped on Vertex AI.
272 pub fn include_thoughts(mut self) -> Self {
273 self.config = self.config.include_thoughts();
274 self
275 }
276
277 /// Enable affective dialog (emotionally expressive responses).
278 pub fn affective_dialog(mut self, enabled: bool) -> Self {
279 self.config = self.config.affective_dialog(enabled);
280 self
281 }
282
283 /// Enable proactive audio.
284 pub fn proactive_audio(mut self, enabled: bool) -> Self {
285 self.config = self.config.proactive_audio(enabled);
286 self
287 }
288
289 /// Set media resolution for video/image input.
290 pub fn media_resolution(mut self, res: MediaResolution) -> Self {
291 self.config = self.config.media_resolution(res);
292 self
293 }
294
295 // -- VAD & Activity --
296
297 /// Configure server-side VAD.
298 pub fn vad(mut self, detection: AutomaticActivityDetection) -> Self {
299 self.config = self.config.server_vad(detection);
300 self
301 }
302
303 /// Set activity handling mode (interrupts vs no-interruption).
304 pub fn activity_handling(mut self, handling: ActivityHandling) -> Self {
305 self.config = self.config.activity_handling(handling);
306 self
307 }
308
309 /// Set turn coverage mode.
310 pub fn turn_coverage(mut self, coverage: TurnCoverage) -> Self {
311 self.config = self.config.turn_coverage(coverage);
312 self
313 }
314
315 // -- Session Lifecycle --
316
317 /// Enable session resumption.
318 pub fn session_resume(mut self, enabled: bool) -> Self {
319 if enabled {
320 self.config = self.config.session_resumption(None);
321 }
322 self
323 }
324
325 /// Enable context window compression.
326 pub fn context_compression(mut self, trigger_tokens: u32, target_tokens: u32) -> Self {
327 self.config = self
328 .config
329 .context_window_compression(target_tokens)
330 .context_window_trigger_tokens(trigger_tokens);
331 self
332 }
333
334 // -- Control Plane --
335
336 /// Enable soft turn detection for proactive silence awareness.
337 ///
338 /// When `proactiveAudio` is enabled, the model may choose not to respond.
339 /// After VAD end, if the model stays silent for `timeout`, a lightweight
340 /// "soft turn" updates state and fires watchers without forcing a response.
341 pub fn soft_turn_timeout(mut self, timeout: Duration) -> Self {
342 self.soft_turn_timeout = Some(timeout);
343 self
344 }
345
346 /// Set the steering mode for how the phase machine delivers instructions.
347 ///
348 /// - `InstructionUpdate` (default): Replace system instruction on transition.
349 /// - `ContextInjection`: Inject steering via `send_client_content`.
350 /// - `Hybrid`: Instruction on transition, context injection per turn.
351 pub fn steering_mode(mut self, mode: SteeringMode) -> Self {
352 self.steering_mode = mode;
353 self
354 }
355
356 /// Set when model-role context turns are delivered to the wire.
357 ///
358 /// - `Immediate` (default): Send as a single batched frame during
359 /// TurnComplete processing.
360 /// - `Deferred`: Queue context and flush before the next user send
361 /// (`send_audio`/`send_text`/`send_video`). Eliminates isolated
362 /// WebSocket frames during silence that can confuse the model.
363 ///
364 /// ```ignore
365 /// Live::builder()
366 /// .steering_mode(SteeringMode::ContextInjection)
367 /// .context_delivery(ContextDelivery::Deferred)
368 /// .phase("greeting")
369 /// .instruction("Welcome the guest")
370 /// .done()
371 /// .initial_phase("greeting")
372 /// ```
373 pub fn context_delivery(mut self, mode: ContextDelivery) -> Self {
374 self.context_delivery = mode;
375 self
376 }
377
378 /// Enable the conversation repair protocol.
379 ///
380 /// Tracks unfulfilled `needs` per phase. After `nudge_after` stalled turns,
381 /// injects a gentle nudge. After `escalate_after` turns, sets
382 /// `repair:escalation` in state for phase guards to handle.
383 pub fn repair(mut self, config: RepairConfig) -> Self {
384 self.repair_config = Some(config);
385 self
386 }
387
388 /// Set a session persistence backend for surviving process restarts.
389 pub fn persistence(mut self, backend: Arc<dyn SessionPersistence>) -> Self {
390 self.persistence = Some(backend);
391 self
392 }
393
394 /// Set the session ID for persistence.
395 pub fn session_id(mut self, id: impl Into<String>) -> Self {
396 self.session_id = Some(id.into());
397 self
398 }
399
400 /// Enable or disable tool availability advisory on phase transitions.
401 ///
402 /// When enabled (default), the SDK injects a model-role context turn
403 /// telling the model which tools are available in the new phase.
404 pub fn tool_advisory(mut self, enabled: bool) -> Self {
405 self.tool_advisory = enabled;
406 self
407 }
408}