Skip to main content

zai_rs/model/
traits.rs

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