Skip to main content

zai_rs/knowledge/
update.rs

1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4use validator::Validate;
5
6use super::create::{BackgroundColor, EmbeddingId, KnowledgeIcon};
7use crate::client::http::HttpClient;
8
9/// Update body for editing a knowledge base
10#[derive(Debug, Clone, Default, Serialize, Deserialize, Validate)]
11pub struct UpdateKnowledgeBody {
12    /// Embedding model id (3 or 11)
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub embedding_id: Option<EmbeddingId>,
15    /// Knowledge base name
16    #[serde(skip_serializing_if = "Option::is_none")]
17    #[validate(length(min = 1))]
18    pub name: Option<String>,
19    /// Knowledge base description
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub description: Option<String>,
22    /// Background color
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub background: Option<BackgroundColor>,
25    /// Icon name
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub icon: Option<KnowledgeIcon>,
28    /// Callback URL when rebuilding is required
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub callback_url: Option<String>,
31    /// Callback headers as key-value
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub callback_header: Option<HashMap<String, String>>,
34}
35
36impl UpdateKnowledgeBody {
37    /// Returns true if no fields are set
38    fn is_empty(&self) -> bool {
39        self.embedding_id.is_none()
40            && self.name.is_none()
41            && self.description.is_none()
42            && self.background.is_none()
43            && self.icon.is_none()
44            && self.callback_url.is_none()
45            && self.callback_header.is_none()
46    }
47}
48
49/// Knowledge update request (PUT /llm-application/open/knowledge/{id})
50pub struct KnowledgeUpdateRequest {
51    /// Bearer API key
52    pub key: String,
53    url: String,
54    body: UpdateKnowledgeBody,
55}
56
57impl KnowledgeUpdateRequest {
58    /// Build update request targeting a specific id with empty body
59    pub fn new(key: String, id: impl AsRef<str>) -> Self {
60        let url = format!(
61            "https://open.bigmodel.cn/api/llm-application/open/knowledge/{}",
62            id.as_ref()
63        );
64        Self {
65            key,
66            url,
67            body: UpdateKnowledgeBody::default(),
68        }
69    }
70
71    /// Setters to update individual fields
72    pub fn with_embedding_id(mut self, id: EmbeddingId) -> Self {
73        self.body.embedding_id = Some(id);
74        self
75    }
76    pub fn with_name(mut self, name: impl Into<String>) -> Self {
77        self.body.name = Some(name.into());
78        self
79    }
80    pub fn with_description(mut self, desc: impl Into<String>) -> Self {
81        self.body.description = Some(desc.into());
82        self
83    }
84    pub fn with_background(mut self, bg: BackgroundColor) -> Self {
85        self.body.background = Some(bg);
86        self
87    }
88    pub fn with_icon(mut self, icon: KnowledgeIcon) -> Self {
89        self.body.icon = Some(icon);
90        self
91    }
92    pub fn with_callback_url(mut self, url: impl Into<String>) -> Self {
93        self.body.callback_url = Some(url.into());
94        self
95    }
96    pub fn with_callback_header(mut self, headers: HashMap<String, String>) -> Self {
97        self.body.callback_header = Some(headers);
98        self
99    }
100
101    pub async fn send(&self) -> crate::ZaiResult<KnowledgeUpdateResponse> {
102        if self.body.is_empty() {
103            return Err(crate::client::error::ZaiError::ApiError {
104                code: 1200,
105                message: "update body is empty; set at least one field".to_string(),
106            });
107        }
108
109        self.body.validate()?;
110        let resp = self.put().await?;
111        let parsed = resp.json::<KnowledgeUpdateResponse>().await?;
112        Ok(parsed)
113    }
114
115    pub fn put(
116        &self,
117    ) -> impl std::future::Future<Output = crate::ZaiResult<reqwest::Response>> + Send {
118        let url = self.url.clone();
119        let key = self.key.clone();
120        let body = self.body.clone();
121        async move {
122            let body_str = serde_json::to_string(&body)?;
123            let resp = reqwest::Client::new()
124                .put(url)
125                .bearer_auth(key)
126                .header("Content-Type", "application/json")
127                .body(body_str)
128                .send()
129                .await?;
130            let status = resp.status();
131            if status.is_success() {
132                return Ok(resp);
133            }
134            // Non-success: try parse standard error envelope {"error": {code, message}}
135            let text = resp.text().await.unwrap_or_default();
136            #[derive(serde::Deserialize)]
137            struct ErrEnv {
138                error: ErrObj,
139            }
140            #[derive(serde::Deserialize)]
141            struct ErrObj {
142                _code: serde_json::Value,
143                message: String,
144            }
145
146            if let Ok(parsed) = serde_json::from_str::<ErrEnv>(&text) {
147                return Err(crate::client::error::ZaiError::from_api_response(
148                    status.as_u16(),
149                    0,
150                    parsed.error.message,
151                ));
152            }
153
154            Err(crate::client::error::ZaiError::from_api_response(
155                status.as_u16(),
156                0,
157                text,
158            ))
159        }
160    }
161}
162
163impl HttpClient for KnowledgeUpdateRequest {
164    type Body = UpdateKnowledgeBody;
165    type ApiUrl = String;
166    type ApiKey = String;
167
168    fn api_url(&self) -> &Self::ApiUrl {
169        &self.url
170    }
171    fn api_key(&self) -> &Self::ApiKey {
172        &self.key
173    }
174    fn body(&self) -> &Self::Body {
175        &self.body
176    }
177}
178
179/// Update response envelope without data
180#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
181pub struct KnowledgeUpdateResponse {
182    #[serde(skip_serializing_if = "Option::is_none")]
183    pub code: Option<i64>,
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub message: Option<String>,
186    #[serde(skip_serializing_if = "Option::is_none")]
187    pub timestamp: Option<u64>,
188}