Skip to main content

zai_rs/model/
traits.rs

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