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}