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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum EmbeddingId {
17 Embedding2,
19 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#[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#[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#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
80pub struct CreateKnowledgeBody {
81 pub embedding_id: EmbeddingId,
83 #[validate(length(min = 1))]
85 pub name: String,
86 #[serde(skip_serializing_if = "Option::is_none")]
88 pub description: Option<String>,
89 #[serde(skip_serializing_if = "Option::is_none")]
91 pub background: Option<BackgroundColor>,
92 #[serde(skip_serializing_if = "Option::is_none")]
94 pub icon: Option<KnowledgeIcon>,
95}
96
97pub struct CreateKnowledgeRequest {
99 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 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 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 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#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
199pub struct CreateKnowledgeResponseData {
200 #[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}