use std::marker::PhantomData;
use std::sync::Arc;
use serde::Serialize;
use validator::Validate;
use super::super::{
chat_base_request::*, chat_base_response::ChatCompletionResponse, tools::*, traits::*,
};
use crate::client::ZaiClient;
pub struct ChatCompletion<N, M, S = StreamOff>
where
N: ModelName + Chat,
(N, M): Bounded,
ChatBody<N, M>: Serialize,
S: StreamState,
{
body: ChatBody<N, M>,
_stream: PhantomData<S>,
}
impl<N, M> ChatCompletion<N, M, StreamOff>
where
N: ModelName + Chat,
(N, M): Bounded,
ChatBody<N, M>: Serialize,
{
pub fn new(model: N, messages: M) -> ChatCompletion<N, M, StreamOff> {
let body = ChatBody::new(model, messages);
ChatCompletion {
body,
_stream: PhantomData,
}
}
pub fn body_mut(&mut self) -> &mut ChatBody<N, M> {
&mut self.body
}
pub fn add_messages(mut self, messages: M) -> Self {
self.body = self.body.add_messages(messages);
self
}
pub fn with_request_id(mut self, request_id: impl Into<String>) -> Self {
self.body = self.body.with_request_id(request_id);
self
}
pub fn with_do_sample(mut self, do_sample: bool) -> Self {
self.body = self.body.with_do_sample(do_sample);
self
}
pub fn with_temperature(mut self, temperature: f64) -> Self {
self.body = self.body.with_temperature(temperature);
self
}
pub fn with_top_p(mut self, top_p: f64) -> Self {
self.body = self.body.with_top_p(top_p);
self
}
pub fn with_max_tokens(mut self, max_tokens: u32) -> Self {
self.body = self.body.with_max_tokens(max_tokens);
self
}
pub fn add_tool(mut self, tool: Tools) -> Self {
self.body = self.body.add_tool(tool);
self
}
pub fn add_tools(mut self, tools: Vec<Tools>) -> Self {
self.body = self.body.extend_tools(tools);
self
}
pub fn with_tool_choice(mut self, tool_choice: ToolChoice) -> Self {
self.body = self.body.with_tool_choice(tool_choice);
self
}
pub fn with_response_format(mut self, format: ResponseFormat) -> Self {
self.body = self.body.with_response_format(format);
self
}
pub fn with_user_id(mut self, user_id: impl Into<String>) -> Self {
self.body = self.body.with_user_id(user_id);
self
}
pub fn with_stop(mut self, stop: String) -> Self {
self.body = self.body.with_stop(stop);
self
}
pub fn with_thinking(mut self, thinking: ThinkingType) -> Self
where
N: ThinkEnable,
{
self.body = self.body.with_thinking(thinking);
self
}
pub fn with_reasoning_effort(mut self, effort: ReasoningEffort) -> Self
where
N: ReasoningEffortEnable,
{
self.body = self.body.with_reasoning_effort(effort);
self
}
pub fn enable_stream(mut self) -> ChatCompletion<N, M, StreamOn> {
self.body.stream = Some(true);
ChatCompletion {
body: self.body,
_stream: PhantomData,
}
}
pub fn validate(&self) -> crate::ZaiResult<()> {
self.body
.validate()
.map_err(crate::client::error::ZaiError::from)?;
if matches!(self.body.stream, Some(true)) {
return Err(crate::client::error::ZaiError::ApiError {
code: crate::client::error::codes::SDK_VALIDATION,
message: "stream=true detected; use enable_stream() and streaming APIs instead"
.to_string(),
});
}
Ok(())
}
pub async fn send_via(&self, client: &ZaiClient) -> crate::ZaiResult<ChatCompletionResponse>
where
N: serde::Serialize,
M: serde::Serialize,
{
self.validate()?;
let route = crate::client::routes::CHAT_COMPLETE;
let url = client.endpoints().resolve_route(route, &[])?;
client
.send_json::<_, ChatCompletionResponse>(route.method(), url, &self.body)
.await
}
pub async fn send_via_coding_plan(
&self,
client: &ZaiClient,
) -> crate::ZaiResult<ChatCompletionResponse>
where
N: serde::Serialize,
M: serde::Serialize,
{
self.validate()?;
let route = crate::client::routes::CHAT_COMPLETE_CODING;
let url = client.endpoints().resolve_route(route, &[])?;
client
.send_json::<_, ChatCompletionResponse>(route.method(), url, &self.body)
.await
}
}
impl<N, M> ChatCompletion<N, M, StreamOn>
where
N: ModelName + Chat,
(N, M): Bounded,
ChatBody<N, M>: Serialize,
{
pub fn with_tool_stream(mut self, tool_stream: bool) -> Self
where
N: ToolStreamEnable,
{
self.body = self.body.with_tool_stream(tool_stream);
self
}
pub fn disable_stream(mut self) -> ChatCompletion<N, M, StreamOff> {
self.body.stream = Some(false);
self.body.tool_stream = None;
ChatCompletion {
body: self.body,
_stream: PhantomData,
}
}
pub fn body(&self) -> &ChatBody<N, M> {
&self.body
}
pub fn prepare_stream_via(
&self,
client: &ZaiClient,
) -> crate::ZaiResult<(String, Vec<u8>, String)>
where
N: serde::Serialize,
M: serde::Serialize,
{
self.body
.validate()
.map_err(crate::client::error::ZaiError::from)?;
let url = client
.endpoints()
.resolve_route(crate::client::routes::CHAT_COMPLETE, &[])?;
let body_bytes =
serde_json::to_vec(&self.body).map_err(|e| crate::ZaiError::JsonError(Arc::new(e)))?;
Ok((url, body_bytes, client.secret().expose().to_string()))
}
}