gemini_genai_rs/quick.rs
1//! Quick-start convenience functions for connecting to the Gemini Multimodal Live API.
2//!
3//! These are thin wrappers over [`SessionConfig`] + [`connect()`] that provide
4//! sensible defaults for the common case. For advanced configuration (custom
5//! transport, codec, modalities, etc.), use [`SessionConfig`] directly.
6//!
7//! # Google AI (API key)
8//!
9//! ```rust,no_run
10//! # async fn example() -> Result<(), gemini_genai_rs::session::SessionError> {
11//! use gemini_genai_rs::prelude::*;
12//!
13//! let session = gemini_genai_rs::quick_connect("API_KEY", "gemini-2.0-flash-live-001").await?;
14//! session.send_text("What is the speed of light?").await?;
15//! let mut events = session.subscribe();
16//! while let Ok(event) = events.recv().await {
17//! if let SessionEvent::TextDelta(ref text) = event { print!("{text}"); }
18//! if let SessionEvent::TurnComplete = event { break; }
19//! }
20//! # Ok(())
21//! # }
22//! ```
23//!
24//! # Vertex AI
25//!
26//! ```rust,no_run
27//! # async fn example() -> Result<(), gemini_genai_rs::session::SessionError> {
28//! use gemini_genai_rs::prelude::*;
29//!
30//! let session = gemini_genai_rs::quick_connect_vertex(
31//! "ya29.ACCESS_TOKEN",
32//! "my-project",
33//! "us-central1",
34//! "gemini-2.0-flash-live-001",
35//! ).await?;
36//! # Ok(())
37//! # }
38//! ```
39
40use crate::protocol::types::{GeminiModel, SessionConfig};
41use crate::session::{SessionError, SessionHandle};
42use crate::transport::{connect, TransportConfig};
43
44/// Connect to Gemini Live with minimal configuration.
45///
46/// Uses sensible defaults: [`TransportConfig::default()`], audio output modality.
47/// For advanced configuration, use [`SessionConfig`] + [`connect()`] directly.
48pub async fn quick_connect(api_key: &str, model: &str) -> Result<SessionHandle, SessionError> {
49 let config = SessionConfig::new(api_key).model(GeminiModel::Custom(model.to_string()));
50 connect(config, TransportConfig::default()).await
51}
52
53/// Connect via Vertex AI with minimal configuration.
54///
55/// Uses sensible defaults: [`TransportConfig::default()`], audio output modality.
56/// For advanced configuration, use [`SessionConfig::from_vertex()`] + [`connect()`] directly.
57pub async fn quick_connect_vertex(
58 access_token: &str,
59 project: &str,
60 location: &str,
61 model: &str,
62) -> Result<SessionHandle, SessionError> {
63 let config = SessionConfig::from_vertex(project, location, access_token)
64 .model(GeminiModel::Custom(model.to_string()));
65 connect(config, TransportConfig::default()).await
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71 use crate::session::SessionPhase;
72 use crate::transport::codec::JsonCodec;
73 use crate::transport::connect_with;
74 use crate::transport::ws::MockTransport;
75
76 /// Verify that `quick_connect` builds a valid SessionConfig internally.
77 ///
78 /// We can't call `quick_connect` directly because it opens a real WebSocket.
79 /// Instead we replicate its config construction and verify it works with a mock.
80 #[tokio::test]
81 async fn quick_connect_creates_valid_config() {
82 let config = SessionConfig::new("test-api-key")
83 .model(GeminiModel::Custom("gemini-2.0-flash-live-001".to_string()));
84
85 // Verify the config fields
86 assert_eq!(
87 config.model,
88 GeminiModel::Custom("gemini-2.0-flash-live-001".to_string())
89 );
90
91 // Verify it connects successfully with a mock transport
92 let mut transport = MockTransport::new();
93 transport.script_recv(br#"{"setupComplete":{}}"#.to_vec());
94
95 let transport_config = TransportConfig {
96 max_reconnect_attempts: 0,
97 ..TransportConfig::default()
98 };
99
100 let handle = connect_with(config, transport_config, transport, JsonCodec)
101 .await
102 .unwrap();
103
104 handle.wait_for_phase(SessionPhase::Active).await;
105 assert_eq!(handle.phase(), SessionPhase::Active);
106 }
107
108 /// Verify that `quick_connect_vertex` builds a valid Vertex AI config.
109 #[tokio::test]
110 async fn quick_connect_vertex_creates_valid_config() {
111 let config = SessionConfig::from_vertex("my-project", "us-central1", "ya29.TOKEN")
112 .model(GeminiModel::Custom("gemini-2.0-flash-live-001".to_string()));
113
114 // Verify model
115 assert_eq!(
116 config.model,
117 GeminiModel::Custom("gemini-2.0-flash-live-001".to_string())
118 );
119
120 // Verify Vertex AI endpoint
121 assert!(config.is_vertex(), "should target Vertex AI");
122 let url = config.ws_url();
123 assert!(
124 url.contains("aiplatform.googleapis.com"),
125 "URL should use Vertex AI endpoint"
126 );
127
128 // Verify model URI contains project and location
129 let model_uri = config.model_uri();
130 assert!(
131 model_uri.contains("my-project"),
132 "model URI should contain project ID"
133 );
134 assert!(
135 model_uri.contains("us-central1"),
136 "model URI should contain location"
137 );
138
139 // Verify bearer token is set
140 assert_eq!(config.bearer_token(), Some("ya29.TOKEN"));
141
142 // Verify it connects successfully with a mock transport
143 let mut transport = MockTransport::new();
144 transport.script_recv(br#"{"setupComplete":{}}"#.to_vec());
145
146 let transport_config = TransportConfig {
147 max_reconnect_attempts: 0,
148 ..TransportConfig::default()
149 };
150
151 let handle = connect_with(config, transport_config, transport, JsonCodec)
152 .await
153 .unwrap();
154
155 handle.wait_for_phase(SessionPhase::Active).await;
156 assert_eq!(handle.phase(), SessionPhase::Active);
157 }
158}