Skip to main content

zai_rs/knowledge/
create.rs

1use std::sync::Arc;
2
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4use validator::Validate;
5
6use crate::{
7    ZaiResult,
8    client::{
9        endpoints::{ApiBase, EndpointConfig, paths},
10        http::{HttpClient, HttpClientConfig, parse_typed_response},
11    },
12};
13
14/// Embedding model id enum mapped to integer ids
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum EmbeddingId {
17    /// 3: Embedding-2
18    Embedding2,
19    /// 11: Embedding-3-new
20    Embedding3New,
21}
22
23impl EmbeddingId {
24    pub fn as_i64(&self) -> i64 {
25        match self {
26            EmbeddingId::Embedding2 => 3,
27            EmbeddingId::Embedding3New => 11,
28        }
29    }
30}
31
32impl Serialize for EmbeddingId {
33    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
34        serializer.serialize_i64(self.as_i64())
35    }
36}
37
38impl<'de> Deserialize<'de> for EmbeddingId {
39    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
40        let v = i64::deserialize(deserializer)?;
41        match v {
42            3 => Ok(EmbeddingId::Embedding2),
43            11 => Ok(EmbeddingId::Embedding3New),
44            other => Err(serde::de::Error::custom(format!(
45                "unsupported embedding_id: {} (expected 3 or 11)",
46                other
47            ))),
48        }
49    }
50}
51
52/// Background color enum
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
54#[serde(rename_all = "lowercase")]
55pub enum BackgroundColor {
56    Blue,
57    Red,
58    Orange,
59    Purple,
60    Sky,
61    Green,
62    Yellow,
63}
64
65/// Knowledge icon enum
66#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
67#[serde(rename_all = "lowercase")]
68pub enum KnowledgeIcon {
69    Question,
70    Book,
71    Seal,
72    Wrench,
73    Tag,
74    Horn,
75    House,
76}
77
78/// Request body for creating a knowledge base
79#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
80pub struct CreateKnowledgeBody {
81    /// Embedding model id (3 or 11)
82    pub embedding_id: EmbeddingId,
83    /// Knowledge base name
84    #[validate(length(min = 1))]
85    pub name: String,
86    /// Knowledge base description (optional)
87    #[serde(skip_serializing_if = "Option::is_none")]
88    pub description: Option<String>,
89    /// Background color (optional; default blue on server)
90    #[serde(skip_serializing_if = "Option::is_none")]
91    pub background: Option<BackgroundColor>,
92    /// Icon name (optional; default question on server)
93    #[serde(skip_serializing_if = "Option::is_none")]
94    pub icon: Option<KnowledgeIcon>,
95}
96
97/// Create knowledge request (POST /llm-application/open/knowledge)
98pub struct CreateKnowledgeRequest {
99    /// Bearer key
100    pub key: String,
101    url: String,
102    endpoint_config: EndpointConfig,
103    api_base: ApiBase,
104    http_config: Arc<HttpClientConfig>,
105    body: CreateKnowledgeBody,
106}
107
108impl CreateKnowledgeRequest {
109    /// Build a create request with required fields
110    pub fn new(key: String, embedding_id: EmbeddingId, name: impl Into<String>) -> Self {
111        let endpoint_config = EndpointConfig::default();
112        let api_base = ApiBase::LlmApplication;
113        let url = endpoint_config.url(&api_base, paths::KNOWLEDGE);
114        let body = CreateKnowledgeBody {
115            embedding_id,
116            name: name.into(),
117            description: None,
118            background: None,
119            icon: None,
120        };
121        Self {
122            key,
123            url,
124            endpoint_config,
125            api_base,
126            http_config: Arc::new(HttpClientConfig::default()),
127            body,
128        }
129    }
130
131    fn rebuild_url(&mut self) {
132        self.url = self.endpoint_config.url(&self.api_base, paths::KNOWLEDGE);
133    }
134
135    pub fn with_base_url(mut self, base: impl Into<String>) -> Self {
136        self.api_base = ApiBase::Custom(base.into());
137        self.rebuild_url();
138        self
139    }
140
141    pub fn with_endpoint_config(mut self, endpoint_config: EndpointConfig) -> Self {
142        self.endpoint_config = endpoint_config;
143        self.rebuild_url();
144        self
145    }
146
147    pub fn with_http_config(mut self, config: HttpClientConfig) -> Self {
148        self.http_config = Arc::new(config);
149        self
150    }
151
152    /// Optional fields setters
153    pub fn with_description(mut self, desc: impl Into<String>) -> Self {
154        self.body.description = Some(desc.into());
155        self
156    }
157    pub fn with_background(mut self, bg: BackgroundColor) -> Self {
158        self.body.background = Some(bg);
159        self
160    }
161    pub fn with_icon(mut self, icon: KnowledgeIcon) -> Self {
162        self.body.icon = Some(icon);
163        self
164    }
165
166    /// Validate and send, returning typed response
167    pub async fn send(&self) -> ZaiResult<CreateKnowledgeResponse> {
168        self.body.validate()?;
169
170        let resp: reqwest::Response = self.post().await?;
171
172        let parsed = parse_typed_response::<CreateKnowledgeResponse>(resp).await?;
173        Ok(parsed)
174    }
175}
176
177impl HttpClient for CreateKnowledgeRequest {
178    type Body = CreateKnowledgeBody;
179    type ApiUrl = String;
180    type ApiKey = String;
181
182    fn api_url(&self) -> &Self::ApiUrl {
183        &self.url
184    }
185    fn api_key(&self) -> &Self::ApiKey {
186        &self.key
187    }
188    fn body(&self) -> &Self::Body {
189        &self.body
190    }
191
192    fn http_config(&self) -> Arc<HttpClientConfig> {
193        Arc::clone(&self.http_config)
194    }
195}
196
197/// Response of knowledge creation
198#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
199pub struct CreateKnowledgeResponseData {
200    /// Newly created id
201    #[serde(skip_serializing_if = "Option::is_none")]
202    pub id: Option<String>,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
206pub struct CreateKnowledgeResponse {
207    #[serde(skip_serializing_if = "Option::is_none")]
208    pub data: Option<CreateKnowledgeResponseData>,
209    #[serde(skip_serializing_if = "Option::is_none")]
210    pub code: Option<i64>,
211    #[serde(skip_serializing_if = "Option::is_none")]
212    pub message: Option<String>,
213    #[serde(skip_serializing_if = "Option::is_none")]
214    pub timestamp: Option<u64>,
215}