Skip to main content

zai_rs/agent/
request.rs

1//! Shared, closed Agent v1 request values.
2
3use serde::{Deserialize, Serialize};
4
5/// Agent identifiers accepted by the frozen `/v1/agents` request schema.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
7#[serde(rename_all = "snake_case")]
8pub enum AgentId {
9    /// General-purpose translation agent.
10    GeneralTranslation,
11    /// Slide-deck generation agent.
12    SlidesGlmAgent,
13    /// AI drawing agent.
14    AiDrawingAgent,
15    /// Receipt-recognition agent.
16    ReceiptRecognitionAgent,
17    /// Clothing-recognition agent.
18    ClothesRecognitionAgent,
19    /// Education problem-solving agent.
20    IntelligentEducationSolveAgent,
21    /// Vidu template agent.
22    ViduTemplateAgent,
23}
24
25impl AgentId {
26    /// Return the exact identifier serialized on the wire.
27    pub const fn as_str(self) -> &'static str {
28        match self {
29            Self::GeneralTranslation => "general_translation",
30            Self::SlidesGlmAgent => "slides_glm_agent",
31            Self::AiDrawingAgent => "ai_drawing_agent",
32            Self::ReceiptRecognitionAgent => "receipt_recognition_agent",
33            Self::ClothesRecognitionAgent => "clothes_recognition_agent",
34            Self::IntelligentEducationSolveAgent => "intelligent_education_solve_agent",
35            Self::ViduTemplateAgent => "vidu_template_agent",
36        }
37    }
38}
39
40impl std::fmt::Display for AgentId {
41    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        formatter.write_str(self.as_str())
43    }
44}
45
46/// Roles accepted in Agent v1 request messages.
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
48#[serde(rename_all = "lowercase")]
49pub enum AgentRole {
50    /// System instruction.
51    System,
52    /// End-user input.
53    User,
54    /// Assistant-authored context.
55    Assistant,
56}
57
58/// Request content-part discriminator.
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
60#[serde(rename_all = "snake_case")]
61pub enum AgentRequestContentType {
62    /// Text content.
63    Text,
64    /// Previously uploaded file identifier.
65    FileId,
66    /// Remote file URL.
67    FileUrl,
68    /// Remote image URL.
69    ImageUrl,
70}
71
72/// One typed multimodal content part in an Agent v1 request.
73#[derive(Clone, Serialize, Deserialize)]
74#[serde(deny_unknown_fields)]
75pub struct AgentRequestContentPart {
76    #[serde(rename = "type")]
77    type_: AgentRequestContentType,
78    #[serde(skip_serializing_if = "Option::is_none")]
79    text: Option<String>,
80    #[serde(skip_serializing_if = "Option::is_none")]
81    file_id: Option<String>,
82    #[serde(skip_serializing_if = "Option::is_none")]
83    file_url: Option<String>,
84    #[serde(skip_serializing_if = "Option::is_none")]
85    image_url: Option<String>,
86}
87
88impl std::fmt::Debug for AgentRequestContentPart {
89    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90        formatter
91            .debug_struct("AgentRequestContentPart")
92            .field("type", &self.type_)
93            .field("value", &"[REDACTED]")
94            .finish()
95    }
96}
97
98impl AgentRequestContentPart {
99    fn with_value(type_: AgentRequestContentType, value: String) -> Self {
100        let mut part = Self {
101            type_,
102            text: None,
103            file_id: None,
104            file_url: None,
105            image_url: None,
106        };
107        match type_ {
108            AgentRequestContentType::Text => part.text = Some(value),
109            AgentRequestContentType::FileId => part.file_id = Some(value),
110            AgentRequestContentType::FileUrl => part.file_url = Some(value),
111            AgentRequestContentType::ImageUrl => part.image_url = Some(value),
112        }
113        part
114    }
115
116    /// Construct a text part.
117    pub fn text(value: impl Into<String>) -> Self {
118        Self::with_value(AgentRequestContentType::Text, value.into())
119    }
120
121    /// Construct an uploaded-file identifier part.
122    pub fn file_id(value: impl Into<String>) -> Self {
123        Self::with_value(AgentRequestContentType::FileId, value.into())
124    }
125
126    /// Construct a remote-file URL part.
127    pub fn file_url(value: impl Into<String>) -> Self {
128        Self::with_value(AgentRequestContentType::FileUrl, value.into())
129    }
130
131    /// Construct a remote-image URL part.
132    pub fn image_url(value: impl Into<String>) -> Self {
133        Self::with_value(AgentRequestContentType::ImageUrl, value.into())
134    }
135
136    /// Return the content-part discriminator.
137    pub const fn type_(&self) -> AgentRequestContentType {
138        self.type_
139    }
140}
141
142/// Agent request content in one of the three frozen wire forms.
143#[derive(Clone, Serialize, Deserialize)]
144#[serde(untagged)]
145pub enum AgentContent {
146    /// Plain text content.
147    Text(String),
148    /// One multimodal content part.
149    Part(AgentRequestContentPart),
150    /// Multiple multimodal content parts.
151    Parts(Vec<AgentRequestContentPart>),
152}
153
154impl std::fmt::Debug for AgentContent {
155    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156        match self {
157            Self::Text(_) => formatter.write_str("AgentContent::Text([REDACTED])"),
158            Self::Part(part) => formatter
159                .debug_tuple("AgentContent::Part")
160                .field(part)
161                .finish(),
162            Self::Parts(parts) => formatter
163                .debug_struct("AgentContent::Parts")
164                .field("count", &parts.len())
165                .finish(),
166        }
167    }
168}
169
170impl From<String> for AgentContent {
171    fn from(value: String) -> Self {
172        Self::Text(value)
173    }
174}
175
176impl From<&str> for AgentContent {
177    fn from(value: &str) -> Self {
178        Self::Text(value.to_owned())
179    }
180}
181
182impl From<AgentRequestContentPart> for AgentContent {
183    fn from(value: AgentRequestContentPart) -> Self {
184        Self::Part(value)
185    }
186}
187
188impl From<Vec<AgentRequestContentPart>> for AgentContent {
189    fn from(value: Vec<AgentRequestContentPart>) -> Self {
190        Self::Parts(value)
191    }
192}
193
194/// One message in an Agent v1 invocation request.
195#[derive(Clone, Serialize, Deserialize)]
196#[serde(deny_unknown_fields)]
197pub struct AgentMessage {
198    role: AgentRole,
199    content: AgentContent,
200}
201
202impl std::fmt::Debug for AgentMessage {
203    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204        formatter
205            .debug_struct("AgentMessage")
206            .field("role", &self.role)
207            .field("content", &"[REDACTED]")
208            .finish()
209    }
210}
211
212impl AgentMessage {
213    /// Construct a message with an explicit typed role.
214    pub fn new(role: AgentRole, content: impl Into<AgentContent>) -> Self {
215        Self {
216            role,
217            content: content.into(),
218        }
219    }
220
221    /// Construct a user message.
222    pub fn user(content: impl Into<AgentContent>) -> Self {
223        Self::new(AgentRole::User, content)
224    }
225
226    /// Construct a system message.
227    pub fn system(content: impl Into<AgentContent>) -> Self {
228        Self::new(AgentRole::System, content)
229    }
230
231    /// Construct an assistant message.
232    pub fn assistant(content: impl Into<AgentContent>) -> Self {
233        Self::new(AgentRole::Assistant, content)
234    }
235
236    /// Return the message role.
237    pub const fn role(&self) -> AgentRole {
238        self.role
239    }
240
241    /// Borrow the typed message content.
242    pub const fn content(&self) -> &AgentContent {
243        &self.content
244    }
245}