use serde::{Deserialize, Serialize};
use validator::Validate;
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct AgentCreateRequest {
#[validate(length(min = 1, max = 100))]
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
#[validate(length(max = 1000))]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub system_prompt: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<Vec<serde_json::Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub config: Option<AgentConfig>,
}
impl AgentCreateRequest {
pub fn builder() -> AgentCreateRequestBuilder {
AgentCreateRequestBuilder::default()
}
}
#[derive(Default)]
pub struct AgentCreateRequestBuilder {
name: Option<String>,
description: Option<String>,
system_prompt: Option<String>,
model: Option<String>,
tools: Option<Vec<serde_json::Value>>,
config: Option<AgentConfig>,
}
impl AgentCreateRequestBuilder {
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
pub fn description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
pub fn system_prompt(mut self, system_prompt: impl Into<String>) -> Self {
self.system_prompt = Some(system_prompt.into());
self
}
pub fn model(mut self, model: impl Into<String>) -> Self {
self.model = Some(model.into());
self
}
pub fn tools(mut self, tools: Vec<serde_json::Value>) -> Self {
self.tools = Some(tools);
self
}
pub fn config(mut self, config: AgentConfig) -> Self {
self.config = Some(config);
self
}
pub fn build(self) -> Result<AgentCreateRequest, crate::client::error::ZaiError> {
let name = self.name.ok_or_else(|| crate::client::error::ZaiError::ApiError {
code: 1200,
message: "name is required".to_string(),
})?;
let req = AgentCreateRequest {
name,
description: self.description,
system_prompt: self.system_prompt,
model: self.model,
tools: self.tools,
config: self.config,
};
req.validate()
.map_err(|e| crate::client::error::ZaiError::ApiError {
code: 1200,
message: format!("Validation error: {:?}", e),
})?;
Ok(req)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct AgentConfig {
#[serde(skip_serializing_if = "Option::is_none")]
#[validate(range(min = 0.0, max = 1.0))]
pub temperature: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thinking_enabled: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct AgentUpdateRequest {
#[serde(skip_serializing_if = "Option::is_none")]
#[validate(length(min = 1, max = 100))]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[validate(length(max = 1000))]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub system_prompt: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<Vec<serde_json::Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub config: Option<AgentConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct AgentChatRequest {
#[validate(length(min = 1))]
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub conversation_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stream: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parameters: Option<AgentChatParameters>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct AgentChatParameters {
#[serde(skip_serializing_if = "Option::is_none")]
#[validate(range(min = 0.0, max = 1.0))]
pub temperature: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thinking_enabled: Option<bool>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_builder_success() {
let req = AgentCreateRequest::builder()
.name("TestAgent")
.description("A test agent")
.model("glm-4.5")
.build()
.unwrap();
assert_eq!(req.name, "TestAgent");
assert_eq!(req.description, Some("A test agent".to_string()));
assert_eq!(req.model, Some("glm-4.5".to_string()));
}
#[test]
fn test_builder_missing_name() {
let result = AgentCreateRequest::builder()
.description("No name")
.build();
assert!(result.is_err());
}
#[test]
fn test_builder_name_too_long() {
let result = AgentCreateRequest::builder()
.name("a".repeat(101))
.build();
assert!(result.is_err());
}
#[test]
fn test_builder_with_config() {
let req = AgentCreateRequest::builder()
.name("ConfigAgent")
.config(AgentConfig {
temperature: Some(0.7),
max_tokens: Some(1000),
thinking_enabled: Some(false),
})
.build()
.unwrap();
assert_eq!(req.config.unwrap().temperature, Some(0.7));
}
#[test]
fn test_serde_roundtrip() {
let req = AgentCreateRequest::builder()
.name("SerdeAgent")
.description("Serde test")
.build()
.unwrap();
let json = serde_json::to_string(&req).unwrap();
let parsed: AgentCreateRequest = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.name, "SerdeAgent");
}
#[test]
fn test_chat_request_validation() {
let req = AgentChatRequest {
message: "Hello".to_string(),
conversation_id: None,
session_id: None,
stream: None,
parameters: None,
};
assert!(req.validate().is_ok());
}
}