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 log::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 buf.extend_from_slice(&bytes);
168 while let Some(pos) = buf.iter().position(|&b| b == b'\n') {
169 let line_vec: Vec<u8> = buf.drain(..=pos).collect();
170 let mut line = &line_vec[..];
171 if line.ends_with(b"\n") {
172 line = &line[..line.len() - 1];
173 }
174 if line.ends_with(b"\r") {
175 line = &line[..line.len() - 1];
176 }
177 if line.is_empty() {
178 continue;
179 }
180 const PREFIX: &[u8] = b"data: ";
181 if line.starts_with(PREFIX) {
182 let rest = &line[PREFIX.len()..];
183 info!("SSE data: {}", String::from_utf8_lossy(rest));
184 if rest == b"[DONE]" {
185 return Ok(());
186 }
187 on_data(rest);
188 }
189 }
190 },
191 Err(e) => {
192 return Err(crate::client::error::ZaiError::NetworkError(
193 std::sync::Arc::new(e),
194 ));
195 },
196 }
197 }
198 Ok(())
199 }
200 }
201}
202
203/// Macro for defining AI model types with standard implementations.
204///
205/// This macro generates a model type with the following implementations:
206/// - `Debug` and `Clone` traits
207/// - `Into<String>` for API identifier conversion
208/// - `Serialize` for JSON serialization
209/// - `ModelName` trait marker
210///
211/// ## Usage Examples
212///
213/// ```rust,ignore
214/// // Basic model definition
215/// define_model_type!(GLM4_5, "glm-4.5");
216/// // Model with attributes
217/// define_model_type!(
218/// #[allow(non_camel_case_types)]
219/// GLM4_5_flash,
220/// "glm-4.5-flash"
221/// );
222/// ```
223#[macro_export]
224macro_rules! define_model_type {
225 ($(#[$meta:meta])* $name:ident, $s:expr) => {
226 #[derive(Debug, Clone)]
227 $(#[$meta])*
228 pub struct $name {}
229
230 impl ::core::convert::From<$name> for String {
231 fn from(_val: $name) -> Self { $s.to_string() }
232 }
233
234 impl ::serde::Serialize for $name {
235 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
236 where S: ::serde::Serializer {
237 let model_name: String = self.clone().into();
238 serializer.serialize_str(&model_name)
239 }
240 }
241
242 impl $crate::model::traits::ModelName for $name {}
243 };
244}
245
246/// Macro for binding message types to AI models.
247///
248/// This macro creates compile-time associations between model types and
249/// message types, ensuring type safety in chat completion requests.
250///
251/// ## Usage Examples
252///
253/// ```rust,ignore
254/// // Single message type binding
255/// impl_message_binding!(GLM4_5, TextMessage);
256/// // Multiple message type bindings
257/// impl_message_binding!(GLM4_5, TextMessage, VisionMessage);
258/// ```
259#[macro_export]
260macro_rules! impl_message_binding {
261 // Single message type
262 ($name:ident, $message_type:ty) => {
263 impl $crate::model::traits::Bounded for ($name, $message_type) {}
264 };
265 // Multiple message types
266 ($name:ident, $message_type:ty, $($message_types:ty),+) => {
267 impl $crate::model::traits::Bounded for ($name, $message_type) {}
268 $(
269 impl $crate::model::traits::Bounded for ($name, $message_types) {}
270 )+
271 };
272}
273
274/// Macro for implementing multiple capability traits on model types.
275///
276/// This macro provides a convenient way to mark models with multiple
277/// capabilities in a single declaration.
278///
279/// ## Usage Examples
280///
281/// ```rust,ignore
282/// // Single model, multiple traits
283/// impl_model_markers!(GLM4_5_flash: AsyncChat, Chat);
284///
285/// // Multiple models, same traits
286/// impl_model_markers!([GLM4_5, GLM4_5_air]: Chat);
287/// ```
288#[macro_export]
289macro_rules! impl_model_markers {
290 // Single model, multiple markers
291 ($model:ident : $($marker:path),+ $(,)?) => {
292 $( impl $marker for $model {} )+
293 };
294 // Multiple models, multiple markers
295 ([$($model:ident),+ ] : $($marker:path),+ $(,)?) => {
296 $( $( impl $marker for $model {} )+ )+
297 };
298}