Skip to main content

zai_rs/knowledge/
update.rs

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