use serde::{Deserialize, Serialize};
use crate::{
ZaiResult,
client::error::{ZaiError, codes},
};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApplicationEnvelope<T> {
pub data: T,
pub code: i64,
pub message: String,
pub timestamp: i64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ApplicationFileStatItem {
pub file_id: String,
pub code: i64,
pub msg: String,
}
pub type ApplicationFileStatsResponse = ApplicationEnvelope<Vec<ApplicationFileStatItem>>;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ApplicationFileUploadSuccessInfo {
pub file_id: String,
pub file_name: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ApplicationFileUploadFailInfo {
pub file_name: String,
pub fail_reason: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ApplicationFileUploadData {
pub success_info: Vec<ApplicationFileUploadSuccessInfo>,
pub fail_info: Vec<ApplicationFileUploadFailInfo>,
}
pub type ApplicationFileUploadResponse = ApplicationEnvelope<ApplicationFileUploadData>;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ApplicationDocumentInfo {
pub id: String,
pub name: String,
pub url: String,
pub dtype: i64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApplicationSlicePosition {
pub x0: f32,
pub x1: f32,
pub top: f32,
pub bottom: f32,
pub page: i64,
pub height: f32,
pub width: f32,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApplicationSliceInfo {
pub document_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<ApplicationSlicePosition>,
#[serde(skip_serializing_if = "Option::is_none")]
pub line: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sheet_name: Option<String>,
pub text: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ApplicationSliceImage {
pub text: String,
pub cos_url: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApplicationDocumentSlices {
pub document: ApplicationDocumentInfo,
pub slice_info: Vec<ApplicationSliceInfo>,
pub hide_positions: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub images: Option<Vec<ApplicationSliceImage>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApplicationSliceInfoData {
pub document_slices: Vec<ApplicationDocumentSlices>,
pub has_old_document: bool,
}
pub type ApplicationSliceInfoResponse = ApplicationEnvelope<ApplicationSliceInfoData>;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ApplicationConversationCreateData {
pub conversation_id: String,
}
pub type ApplicationConversationCreateResponse =
ApplicationEnvelope<ApplicationConversationCreateData>;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApplicationInputTemplate {
pub options: Vec<serde_json::Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApplicationVariable {
pub id: String,
pub name: String,
#[serde(rename = "type")]
pub type_: String,
pub tips: String,
pub allowed_values: Vec<String>,
pub input_template: ApplicationInputTemplate,
}
pub type ApplicationVariablesResponse = ApplicationEnvelope<Vec<ApplicationVariable>>;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ApplicationHistoryData {
pub problems: Vec<String>,
}
pub type ApplicationHistoryResponse = ApplicationEnvelope<ApplicationHistoryData>;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ApplicationMessageData {
#[serde(skip_serializing_if = "Option::is_none")]
pub msg: Option<String>,
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub type_: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cover_url: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApplicationToolCallsData {
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub type_: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_calls_data: Option<serde_json::Map<String, serde_json::Value>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApplicationInvokeEvent {
#[serde(skip_serializing_if = "Option::is_none")]
pub node_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub node_name: Option<String>,
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub type_: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub time: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_calls: Option<ApplicationToolCallsData>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApplicationInvokeDelta {
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<ApplicationMessageData>,
#[serde(skip_serializing_if = "Option::is_none")]
pub event: Option<ApplicationInvokeEvent>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_calls: Option<ApplicationToolCallsData>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApplicationInvokeMessages {
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<ApplicationMessageData>,
#[serde(skip_serializing_if = "Option::is_none")]
pub event: Option<Vec<ApplicationInvokeEvent>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ApplicationInvokeError {
#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub msg: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApplicationInvokeChoice {
#[serde(skip_serializing_if = "Option::is_none")]
pub index: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub finish_reason: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error_msg: Option<ApplicationInvokeError>,
#[serde(skip_serializing_if = "Option::is_none")]
pub delta: Option<ApplicationInvokeDelta>,
#[serde(skip_serializing_if = "Option::is_none")]
pub messages: Option<ApplicationInvokeMessages>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ApplicationInvokeUsage {
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub node_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub input_token_count: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub output_token_count: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub total_token_count: Option<i64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApplicationInvokeResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub request_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub conversation_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub app_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub choices: Option<Vec<ApplicationInvokeChoice>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub usage: Option<Vec<ApplicationInvokeUsage>>,
}
impl ApplicationInvokeResponse {
pub fn validate(&self) -> ZaiResult<()> {
if self.request_id.is_some()
|| self.conversation_id.is_some()
|| self.app_id.is_some()
|| self.choices.is_some()
|| self.usage.is_some()
{
return Ok(());
}
Err(ZaiError::ApiError {
code: codes::SDK_VALIDATION,
message: "application invoke response contained no documented fields".to_owned(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn required_application_envelope_fields_do_not_default() {
let missing_code =
serde_json::from_value::<ApplicationHistoryResponse>(serde_json::json!({
"data": {"problems": []},
"message": "ok",
"timestamp": 1
}));
assert!(missing_code.is_err());
}
#[test]
fn invocation_uses_the_official_top_level_and_camel_case_fields() {
let response: ApplicationInvokeResponse = serde_json::from_value(serde_json::json!({
"request_id": "req-1",
"choices": [{
"index": 0,
"delta": {"content": {"type": "video", "coverUrl": "https://cover"}}
}],
"usage": [{"nodeName": "model", "totalTokenCount": 3}]
}))
.unwrap();
assert_eq!(response.request_id.as_deref(), Some("req-1"));
let choice = &response.choices.as_ref().unwrap()[0];
assert_eq!(
choice
.delta
.as_ref()
.unwrap()
.content
.as_ref()
.unwrap()
.cover_url
.as_deref(),
Some("https://cover")
);
assert_eq!(
response.usage.as_ref().unwrap()[0].total_token_count,
Some(3)
);
assert!(response.validate().is_ok());
assert!(
serde_json::from_value::<ApplicationInvokeResponse>(serde_json::json!({}))
.unwrap()
.validate()
.is_err()
);
}
}