zai_rs/model/
traits.rs

1//! # Core Traits for AI Model Abstractions
2//!
3//! This module defines the fundamental traits that enable type-safe interactions
4//! with different AI models and capabilities in the Zhipu AI ecosystem.
5//!
6//! ## Trait Categories
7//!
8//! ### Model Identification
9//! - [`ModelName`] - Converts model types to string identifiers
10//!
11//! ### Capability Traits
12//! - [`Chat`] - Synchronous chat completion capability
13//! - [`AsyncChat`] - Asynchronous chat completion capability
14//! - [`ThinkEnable`] - Thinking/reasoning capability support
15//! - [`VideoGen`] - Video generation capability
16//! - [`ImageGen`] - Image generation capability
17//! - [`AudioToText`] - Speech recognition capability
18//! - [`TextToSpeech`] - Text-to-speech capability
19//! - [`VoiceClone`] - Voice cloning capability
20//!
21//! ### Type Safety
22//! - [`Bounded`] - Compile-time model-message compatibility verification
23//! - [`StreamState`] - Type-state pattern for streaming capability control
24//!
25//! ## Type-State Pattern
26//!
27//! The `StreamState` trait and its implementations (`StreamOn`, `StreamOff`) provide
28//! compile-time guarantees about streaming capabilities, preventing runtime errors
29//! and enabling better API design.
30
31/// Trait for AI models that can be identified by name.
32///
33/// This trait enables conversion of model types to their string identifiers
34/// used in API requests. All AI model types must implement this trait.
35pub trait ModelName: Into<String> {}
36
37/// Marker trait for compile-time model-message compatibility checking.
38///
39/// This trait is used in conjunction with the type system to ensure that
40/// specific model types are only used with compatible message types,
41/// preventing invalid API calls at compile time.
42pub trait Bounded {}
43
44/// Indicates that a model supports synchronous chat completion.
45///
46/// Models implementing this trait can be used with the chat completion API
47/// API for real-time conversational interactions.
48pub trait Chat {}
49
50/// Indicates that a model supports asynchronous chat completion.
51///
52/// Models implementing this trait can be used with the async chat completion API
53/// API for queued, background processing of conversational requests.
54pub trait AsyncChat {}
55
56/// Indicates that a model supports thinking/reasoning capabilities.
57///
58/// Models implementing this trait can utilize advanced reasoning modes
59/// that show step-by-step thinking processes for complex problem solving.
60pub trait ThinkEnable {}
61
62/// Indicates that a model supports streaming tool calls (tool_stream parameter).
63/// Only models implementing this marker can enable tool_stream in requests.
64pub trait ToolStreamEnable {}
65
66/// Indicates that a model supports video generation.
67///
68/// Models implementing this trait can be used to generate videos from
69/// text descriptions or other inputs.
70pub trait VideoGen {}
71
72/// Indicates that a model supports image generation.
73///
74/// Models implementing this trait can be used to generate images from
75/// text descriptions or other inputs.
76pub trait ImageGen {}
77
78/// Indicates that a model supports speech recognition.
79///
80/// Models implementing this trait can convert audio input to text,
81/// supporting various audio formats and languages.
82pub trait AudioToText {}
83
84/// Indicates that a model supports text-to-speech synthesis.
85///
86/// Models implementing this trait can convert text input to audio output,
87/// supporting various voices and audio formats.
88pub trait TextToSpeech {}
89
90/// Indicates that a model supports voice cloning.
91///
92/// Models implementing this trait can create synthetic voices that
93/// mimic specific speakers based on audio samples.
94pub trait VoiceClone {}
95
96/// Type-state trait for compile-time streaming capability control.
97///
98/// This trait enables the type system to enforce whether a request
99/// supports streaming (`StreamOn`) or non-streaming (`StreamOff`) responses,
100/// preventing invalid API usage patterns.
101pub trait StreamState {}
102
103/// Type-state indicating that streaming is enabled.
104///
105/// Types parameterized with this marker support Server-Sent Events (SSE)
106/// streaming for real-time response processing.
107pub struct StreamOn;
108
109/// Type-state indicating that streaming is disabled.
110///
111/// Types parameterized with this marker receive complete responses
112/// rather than streaming chunks.
113pub struct StreamOff;
114
115impl StreamState for StreamOn {}
116impl StreamState for StreamOff {}
117
118use crate::client::http::HttpClient;
119use futures::StreamExt;
120use log::info;
121
122/// Trait for types that support Server-Sent Events (SSE) streaming.
123///
124/// This trait provides streaming capabilities for API responses that support
125/// real-time data transmission. The default implementation handles SSE protocol
126/// parsing, logging, and callback invocation.
127///
128/// ## Streaming Protocol
129///
130/// The implementation expects SSE-formatted responses with `data: ` prefixed lines.
131/// Each data line is parsed and passed to the callback function. The stream
132/// terminates when a `[DONE]` marker is encountered.
133///
134/// ## Usage
135///
136/// ```rust,ignore
137/// let mut client = ChatCompletion::new(model, messages, api_key).enable_stream();
138/// client.stream_sse_for_each(|data| {
139///     println!("Received: {}", String::from_utf8_lossy(data));
140/// }).await?;
141/// ```
142pub trait SseStreamable: HttpClient {
143    fn stream_sse_for_each<'a, F>(
144        &'a mut self,
145        mut on_data: F,
146    ) -> impl core::future::Future<Output = anyhow::Result<()>> + 'a
147    where
148        F: FnMut(&[u8]) + 'a,
149    {
150        async move {
151            let resp = self.post().await?;
152            let mut stream = resp.bytes_stream();
153            let mut buf: Vec<u8> = Vec::new();
154
155            while let Some(next) = stream.next().await {
156                match next {
157                    Ok(bytes) => {
158                        buf.extend_from_slice(&bytes);
159                        while let Some(pos) = buf.iter().position(|&b| b == b'\n') {
160                            let line_vec: Vec<u8> = buf.drain(..=pos).collect();
161                            let mut line = &line_vec[..];
162                            if line.ends_with(b"\n") {
163                                line = &line[..line.len() - 1];
164                            }
165                            if line.ends_with(b"\r") {
166                                line = &line[..line.len() - 1];
167                            }
168                            if line.is_empty() {
169                                continue;
170                            }
171                            const PREFIX: &[u8] = b"data: ";
172                            if line.starts_with(PREFIX) {
173                                let rest = &line[PREFIX.len()..];
174                                info!("SSE data: {}", String::from_utf8_lossy(rest));
175                                if rest == b"[DONE]" {
176                                    return Ok(());
177                                }
178                                on_data(rest);
179                            }
180                        }
181                    }
182                    Err(e) => return Err(anyhow::anyhow!("Stream error: {}", e)),
183                }
184            }
185            Ok(())
186        }
187    }
188}
189
190/// Macro for defining AI model types with standard implementations.
191///
192/// This macro generates a model type with the following implementations:
193/// - `Debug` and `Clone` traits
194/// - `Into<String>` for API identifier conversion
195/// - `Serialize` for JSON serialization
196/// - `ModelName` trait marker
197///
198/// ## Usage Examples
199///
200/// ```rust,ignore
201/// // Basic model definition
202/// define_model_type!(GLM4_5, "glm-4.5");
203///
204/// // Model with attributes
205/// define_model_type!(
206///     #[allow(non_camel_case_types)]
207///     GLM4_5_flash,
208///     "glm-4.5-flash"
209/// );
210/// ```
211#[macro_export]
212macro_rules! define_model_type {
213    ($(#[$meta:meta])* $name:ident, $s:expr) => {
214        #[derive(Debug, Clone)]
215        $(#[$meta])*
216        pub struct $name {}
217
218        impl ::core::convert::Into<String> for $name {
219            fn into(self) -> String { $s.to_string() }
220        }
221
222        impl ::serde::Serialize for $name {
223            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
224            where S: ::serde::Serializer {
225                let model_name: String = self.clone().into();
226                serializer.serialize_str(&model_name)
227            }
228        }
229
230        impl $crate::model::traits::ModelName for $name {}
231    };
232}
233
234/// Macro for binding message types to AI models.
235///
236/// This macro creates compile-time associations between model types and
237/// message types, ensuring type safety in chat completion requests.
238///
239/// ## Usage Examples
240///
241/// ```rust,ignore
242/// // Single message type binding
243/// impl_message_binding!(GLM4_5, TextMessage);
244///
245/// // Multiple message type bindings
246/// impl_message_binding!(GLM4_5, TextMessage, VisionMessage);
247/// ```
248#[macro_export]
249macro_rules! impl_message_binding {
250    // Single message type
251    ($name:ident, $message_type:ty) => {
252        impl $crate::model::traits::Bounded for ($name, $message_type) {}
253    };
254    // Multiple message types
255    ($name:ident, $message_type:ty, $($message_types:ty),+) => {
256        impl $crate::model::traits::Bounded for ($name, $message_type) {}
257        $(
258            impl $crate::model::traits::Bounded for ($name, $message_types) {}
259        )+
260    };
261}
262
263/// Macro for implementing multiple capability traits on model types.
264///
265/// This macro provides a convenient way to mark models with multiple
266/// capabilities in a single declaration.
267///
268/// ## Usage Examples
269///
270/// ```rust,ignore
271/// // Single model, multiple traits
272/// impl_model_markers!(GLM4_5_flash: AsyncChat, Chat);
273///
274/// // Multiple models, same traits
275/// impl_model_markers!([GLM4_5, GLM4_5_air]: Chat);
276/// ```
277#[macro_export]
278macro_rules! impl_model_markers {
279    // Single model, multiple markers
280    ($model:ident : $($marker:path),+ $(,)?) => {
281        $( impl $marker for $model {} )+
282    };
283    // Multiple models, multiple markers
284    ([$($model:ident),+ ] : $($marker:path),+ $(,)?) => {
285        $( $( impl $marker for $model {} )+ )+
286    };
287}