zai_rs/model/async_chat/
data.rs1use serde::Serialize;
2use validator::Validate;
3
4use super::super::{chat_base_request::*, tools::*, traits::*};
5use crate::{client::ZaiClient, model::async_chat_get::AsyncResponse};
6
7pub struct AsyncChatCompletion<N, M>
11where
12 N: ChatRequestModel + AsyncChat,
13 M: Serialize,
14 (N, M): Bounded,
15 ChatBody<N, M>: Serialize,
16{
17 body: ChatBody<N, M>,
18}
19
20impl<N, M> AsyncChatCompletion<N, M>
21where
22 N: ChatRequestModel + AsyncChat,
23 M: Serialize,
24 (N, M): Bounded,
25 ChatBody<N, M>: Serialize,
26{
27 pub fn new(model: N, messages: M) -> Self {
29 Self {
30 body: ChatBody::new(model, messages),
31 }
32 }
33
34 pub const fn body(&self) -> &ChatBody<N, M> {
36 &self.body
37 }
38
39 pub fn add_message(mut self, message: M) -> Self {
41 self.body = self.body.add_message(message);
42 self
43 }
44 pub fn extend_messages(mut self, messages: impl IntoIterator<Item = M>) -> Self {
46 self.body = self.body.extend_messages(messages);
47 self
48 }
49 pub fn with_request_id(mut self, request_id: impl Into<String>) -> Self {
51 self.body = self.body.with_request_id(request_id);
52 self
53 }
54 pub fn with_do_sample(mut self, do_sample: bool) -> Self {
56 self.body = self.body.with_do_sample(do_sample);
57 self
58 }
59 pub fn with_temperature(mut self, temperature: f64) -> Self {
61 self.body = self.body.with_temperature(temperature);
62 self
63 }
64 pub fn with_top_p(mut self, top_p: f64) -> Self {
66 self.body = self.body.with_top_p(top_p);
67 self
68 }
69 pub fn with_max_tokens(mut self, max_tokens: u32) -> Self {
71 self.body = self.body.with_max_tokens(max_tokens);
72 self
73 }
74 pub fn add_tool(mut self, tool: N::Tool) -> Self
76 where
77 N: ChatToolSupport,
78 {
79 self.body = self.body.add_tool(tool);
80 self
81 }
82 pub fn add_tools(mut self, tools: impl IntoIterator<Item = N::Tool>) -> Self
84 where
85 N: ChatToolSupport,
86 {
87 self.body = self.body.add_tools(tools);
88 self
89 }
90 pub fn with_tool_choice(mut self, tool_choice: ToolChoice) -> Self
92 where
93 N: ChatToolSupport,
94 {
95 self.body = self.body.with_tool_choice(tool_choice);
96 self
97 }
98 pub fn clear_tools(mut self) -> Self
100 where
101 N: ChatToolSupport,
102 {
103 self.body = self.body.clear_tools();
104 self
105 }
106 pub fn with_response_format(mut self, format: ResponseFormat) -> Self
108 where
109 N: ResponseFormatEnable,
110 {
111 self.body = self.body.with_response_format(format);
112 self
113 }
114 pub fn with_watermark_enabled(mut self, enabled: bool) -> Self
116 where
117 N: WatermarkEnable,
118 {
119 self.body = self.body.with_watermark_enabled(enabled);
120 self
121 }
122 pub fn with_user_id(mut self, user_id: impl Into<String>) -> Self {
124 self.body = self.body.with_user_id(user_id);
125 self
126 }
127 pub fn with_stop(mut self, stop: impl Into<String>) -> Self {
129 self.body = self.body.with_stop(stop);
130 self
131 }
132 pub fn with_thinking(mut self, thinking: ThinkingType) -> Self
134 where
135 N: ThinkEnable,
136 {
137 self.body = self.body.with_thinking(thinking);
138 self
139 }
140 pub fn with_reasoning_effort(mut self, effort: ReasoningEffort) -> Self
142 where
143 N: ReasoningEffortEnable,
144 {
145 self.body = self.body.with_reasoning_effort(effort);
146 self
147 }
148
149 pub fn validate(&self) -> crate::ZaiResult<()> {
151 self.body
152 .validate()
153 .map_err(crate::client::error::ZaiError::from)?;
154 Ok(())
155 }
156
157 pub async fn send_via(&self, client: &ZaiClient) -> crate::ZaiResult<AsyncResponse>
159 where
160 N: serde::Serialize,
161 M: serde::Serialize,
162 {
163 self.validate()?;
164 let route = crate::client::routes::CHAT_COMPLETE_ASYNC;
165 let url = client.endpoints().resolve_route(route, &[])?;
166 let response = client
167 .send_json::<_, AsyncResponse>(route.method(), url, &self.body)
168 .await?;
169 response.validate()?;
170 Ok(response)
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177 use crate::model::{
178 chat_message_types::{TextMessage, VoiceMessage},
179 chat_models::{GLM4_voice, GLM5_2},
180 };
181
182 #[test]
183 fn async_text_schema_never_serializes_stream_fields() {
184 let request = AsyncChatCompletion::new(GLM5_2 {}, TextMessage::user("hello"))
185 .add_tool(Tools::Function {
186 function: Function::new("lookup", "Lookup", serde_json::json!({"type": "object"})),
187 })
188 .with_tool_choice(ToolChoice::auto())
189 .with_response_format(ResponseFormat::JsonObject);
190 let json = serde_json::to_value(request.body()).unwrap();
191
192 assert_eq!(json["tool_choice"], "auto");
193 assert_eq!(json["response_format"]["type"], "json_object");
194 assert!(json.get("stream").is_none());
195 assert!(json.get("tool_stream").is_none());
196 assert!(json.get("watermark_enabled").is_none());
197 }
198
199 #[test]
200 fn async_audio_schema_serializes_only_its_specialized_field() {
201 let request = AsyncChatCompletion::new(GLM4_voice {}, VoiceMessage::new_user())
202 .with_watermark_enabled(true);
203 let json = serde_json::to_value(request.body()).unwrap();
204
205 assert_eq!(json["watermark_enabled"], true);
206 for field in [
207 "stream",
208 "tool_stream",
209 "tools",
210 "tool_choice",
211 "response_format",
212 ] {
213 assert!(
214 json.get(field).is_none(),
215 "unexpected async audio field: {field}"
216 );
217 }
218 }
219}