Skip to main content

gemini_genai_rs/session/
traits.rs

1//! Session traits for testability and middleware injection.
2//!
3//! [`SessionWriter`] — write-side: send commands without owning the full handle.
4//! [`SessionReader`] — read-side: subscribe to events and observe phase.
5
6use super::errors::SessionError;
7use super::events::SessionEvent;
8use super::state::SessionPhase;
9use crate::protocol::{Content, ContextUpdate, FunctionResponse};
10use crate::transport::CodecError;
11use async_trait::async_trait;
12use bytes::Bytes;
13use tokio::sync::broadcast;
14
15/// Write-side of a session — send commands without owning the full handle.
16#[async_trait]
17pub trait SessionWriter: Send + Sync + 'static {
18    /// Send raw PCM16 audio bytes.
19    async fn send_audio(&self, data: Bytes) -> Result<(), SessionError>;
20    /// Send a text message.
21    async fn send_text(&self, text: String) -> Result<(), SessionError>;
22    /// Send tool/function call responses back to the model.
23    async fn send_tool_response(
24        &self,
25        responses: Vec<FunctionResponse>,
26    ) -> Result<(), SessionError>;
27    /// Send client content (conversation history or context).
28    async fn send_client_content(
29        &self,
30        turns: Vec<Content>,
31        turn_complete: bool,
32    ) -> Result<(), SessionError>;
33    /// Send a video/image frame (raw JPEG bytes).
34    async fn send_video(&self, jpeg_data: Bytes) -> Result<(), SessionError>;
35    /// Update the system instruction mid-session.
36    async fn update_instruction(&self, instruction: String) -> Result<(), SessionError>;
37    /// Replace the declared tools and/or the system instruction mid-session
38    /// with a `contextUpdate` message; see
39    /// [`SessionHandle::update_context`](super::SessionHandle::update_context).
40    ///
41    /// The default refuses with a codec error, as the session does on a
42    /// model without `contextUpdate`: a writer that cannot reach a live
43    /// session must not report tools as replaced.
44    async fn update_context(&self, update: ContextUpdate) -> Result<(), SessionError> {
45        let _ = update;
46        Err(SessionError::Codec(CodecError::Serialize(
47            "this session writer does not send contextUpdate".into(),
48        )))
49    }
50    /// Signal that user speech activity has started.
51    async fn signal_activity_start(&self) -> Result<(), SessionError>;
52    /// Signal that user speech activity has ended.
53    async fn signal_activity_end(&self) -> Result<(), SessionError>;
54    /// Gracefully disconnect the session.
55    async fn disconnect(&self) -> Result<(), SessionError>;
56}
57
58/// Read-side of a session — subscribe to events and observe phase.
59pub trait SessionReader: Send + Sync + 'static {
60    /// Subscribe to the session event broadcast stream.
61    fn subscribe(&self) -> broadcast::Receiver<SessionEvent>;
62    /// Returns the current session phase.
63    fn phase(&self) -> SessionPhase;
64    /// Returns the unique session ID.
65    fn session_id(&self) -> &str;
66}
67
68#[cfg(test)]
69mod tests {
70    use super::super::handle::SessionHandle;
71    use super::*;
72
73    #[test]
74    fn session_handle_implements_session_writer() {
75        fn assert_impl<T: SessionWriter>() {}
76        assert_impl::<SessionHandle>();
77    }
78
79    #[test]
80    fn session_handle_implements_session_reader() {
81        fn assert_impl<T: SessionReader>() {}
82        assert_impl::<SessionHandle>();
83    }
84
85    #[test]
86    fn session_writer_is_object_safe() {
87        fn _assert(_: &dyn SessionWriter) {}
88    }
89
90    #[test]
91    fn session_reader_is_object_safe() {
92        fn _assert(_: &dyn SessionReader) {}
93    }
94}