1use crate::error::LingerError;
2use crate::RequestId;
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::collections::BTreeMap;
6
7#[derive(Clone, Debug, Serialize, PartialEq)]
10#[non_exhaustive]
11pub struct CreateVectorStoreRequest {
12 #[serde(skip_serializing_if = "Option::is_none")]
15 pub name: Option<String>,
16 #[serde(skip_serializing_if = "Vec::is_empty")]
19 pub file_ids: Vec<String>,
20 #[serde(skip_serializing_if = "BTreeMap::is_empty")]
23 pub metadata: BTreeMap<String, String>,
24 #[serde(flatten)]
27 pub extra: BTreeMap<String, Value>,
28}
29
30impl CreateVectorStoreRequest {
31 pub fn builder() -> CreateVectorStoreRequestBuilder {
34 CreateVectorStoreRequestBuilder::default()
35 }
36}
37
38#[derive(Clone, Debug, Default)]
41#[non_exhaustive]
42pub struct CreateVectorStoreRequestBuilder {
43 name: Option<String>,
44 file_ids: Vec<String>,
45 metadata: BTreeMap<String, String>,
46 extra: BTreeMap<String, Value>,
47}
48
49impl CreateVectorStoreRequestBuilder {
50 pub fn name(mut self, name: impl Into<String>) -> Self {
53 self.name = Some(name.into());
54 self
55 }
56
57 pub fn file_id(mut self, file_id: impl Into<String>) -> Self {
60 self.file_ids.push(file_id.into());
61 self
62 }
63
64 pub fn file_ids(mut self, file_ids: impl IntoIterator<Item = impl Into<String>>) -> Self {
67 self.file_ids = file_ids.into_iter().map(Into::into).collect();
68 self
69 }
70
71 pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
74 self.metadata.insert(key.into(), value.into());
75 self
76 }
77
78 pub fn extra(mut self, name: impl Into<String>, value: Value) -> Self {
81 self.extra.insert(name.into(), value);
82 self
83 }
84
85 pub fn build(self) -> Result<CreateVectorStoreRequest, LingerError> {
88 validate_optional_string("name", self.name.as_deref())?;
89 for file_id in &self.file_ids {
90 if file_id.trim().is_empty() {
91 return Err(LingerError::invalid_config(
92 "file_ids must not contain empty values",
93 ));
94 }
95 }
96 for key in self.metadata.keys() {
97 if key.trim().is_empty() {
98 return Err(LingerError::invalid_config(
99 "metadata keys must not be empty",
100 ));
101 }
102 }
103 Ok(CreateVectorStoreRequest {
104 name: self.name,
105 file_ids: self.file_ids,
106 metadata: self.metadata,
107 extra: self.extra,
108 })
109 }
110}
111
112#[derive(Clone, Debug, Default, Serialize, PartialEq)]
115#[non_exhaustive]
116pub struct ModifyVectorStoreRequest {
117 #[serde(skip_serializing_if = "Option::is_none")]
120 pub name: Option<String>,
121 #[serde(skip_serializing_if = "BTreeMap::is_empty")]
124 pub metadata: BTreeMap<String, String>,
125 #[serde(skip_serializing_if = "Option::is_none")]
128 pub expires_after: Option<Value>,
129}
130
131impl ModifyVectorStoreRequest {
132 pub fn builder() -> ModifyVectorStoreRequestBuilder {
135 ModifyVectorStoreRequestBuilder::default()
136 }
137}
138
139#[derive(Clone, Debug, Default)]
142#[non_exhaustive]
143pub struct ModifyVectorStoreRequestBuilder {
144 name: Option<String>,
145 metadata: BTreeMap<String, String>,
146 expires_after: Option<Value>,
147}
148
149impl ModifyVectorStoreRequestBuilder {
150 pub fn name(mut self, name: impl Into<String>) -> Self {
153 self.name = Some(name.into());
154 self
155 }
156
157 pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
160 self.metadata.insert(key.into(), value.into());
161 self
162 }
163
164 pub fn expires_after(mut self, expires_after: Value) -> Self {
167 self.expires_after = Some(expires_after);
168 self
169 }
170
171 pub fn build(self) -> Result<ModifyVectorStoreRequest, LingerError> {
174 validate_optional_string("name", self.name.as_deref())?;
175 validate_metadata(&self.metadata)?;
176 validate_optional_json_value("expires_after", self.expires_after.as_ref())?;
177 Ok(ModifyVectorStoreRequest {
178 name: self.name,
179 metadata: self.metadata,
180 expires_after: self.expires_after,
181 })
182 }
183}
184
185#[derive(Clone, Debug, Serialize, PartialEq)]
188#[non_exhaustive]
189pub struct CreateVectorStoreFileRequest {
190 pub file_id: String,
193 #[serde(flatten)]
196 pub extra: BTreeMap<String, Value>,
197}
198
199impl CreateVectorStoreFileRequest {
200 pub fn builder() -> CreateVectorStoreFileRequestBuilder {
203 CreateVectorStoreFileRequestBuilder::default()
204 }
205}
206
207#[derive(Clone, Debug, Default)]
210#[non_exhaustive]
211pub struct CreateVectorStoreFileRequestBuilder {
212 file_id: Option<String>,
213 extra: BTreeMap<String, Value>,
214}
215
216impl CreateVectorStoreFileRequestBuilder {
217 pub fn file_id(mut self, file_id: impl Into<String>) -> Self {
220 self.file_id = Some(file_id.into());
221 self
222 }
223
224 pub fn extra(mut self, name: impl Into<String>, value: Value) -> Self {
227 self.extra.insert(name.into(), value);
228 self
229 }
230
231 pub fn build(self) -> Result<CreateVectorStoreFileRequest, LingerError> {
234 Ok(CreateVectorStoreFileRequest {
235 file_id: required_string("file_id", self.file_id)?,
236 extra: self.extra,
237 })
238 }
239}
240
241#[derive(Clone, Debug, Default, Serialize, PartialEq)]
244#[non_exhaustive]
245pub struct ModifyVectorStoreFileRequest {
246 pub attributes: BTreeMap<String, Value>,
249}
250
251impl ModifyVectorStoreFileRequest {
252 pub fn builder() -> ModifyVectorStoreFileRequestBuilder {
255 ModifyVectorStoreFileRequestBuilder::default()
256 }
257}
258
259#[derive(Clone, Debug, Default)]
262#[non_exhaustive]
263pub struct ModifyVectorStoreFileRequestBuilder {
264 attributes: BTreeMap<String, Value>,
265}
266
267impl ModifyVectorStoreFileRequestBuilder {
268 pub fn attribute(mut self, key: impl Into<String>, value: Value) -> Self {
271 self.attributes.insert(key.into(), value);
272 self
273 }
274
275 pub fn attributes(mut self, attributes: BTreeMap<String, Value>) -> Self {
278 self.attributes = attributes;
279 self
280 }
281
282 pub fn build(self) -> Result<ModifyVectorStoreFileRequest, LingerError> {
285 validate_attributes(&self.attributes, true)?;
286 Ok(ModifyVectorStoreFileRequest {
287 attributes: self.attributes,
288 })
289 }
290}
291
292#[derive(Clone, Debug, Serialize, PartialEq)]
295#[non_exhaustive]
296pub struct CreateVectorStoreFileBatchRequest {
297 pub file_ids: Vec<String>,
300 #[serde(flatten)]
303 pub extra: BTreeMap<String, Value>,
304}
305
306impl CreateVectorStoreFileBatchRequest {
307 pub fn builder() -> CreateVectorStoreFileBatchRequestBuilder {
310 CreateVectorStoreFileBatchRequestBuilder::default()
311 }
312}
313
314#[derive(Clone, Debug, Default)]
317#[non_exhaustive]
318pub struct CreateVectorStoreFileBatchRequestBuilder {
319 file_ids: Vec<String>,
320 extra: BTreeMap<String, Value>,
321}
322
323impl CreateVectorStoreFileBatchRequestBuilder {
324 pub fn file_id(mut self, file_id: impl Into<String>) -> Self {
327 self.file_ids.push(file_id.into());
328 self
329 }
330
331 pub fn file_ids(mut self, file_ids: impl IntoIterator<Item = impl Into<String>>) -> Self {
334 self.file_ids = file_ids.into_iter().map(Into::into).collect();
335 self
336 }
337
338 pub fn extra(mut self, name: impl Into<String>, value: Value) -> Self {
341 self.extra.insert(name.into(), value);
342 self
343 }
344
345 pub fn build(self) -> Result<CreateVectorStoreFileBatchRequest, LingerError> {
348 validate_non_empty_values("file_ids", &self.file_ids)?;
349 Ok(CreateVectorStoreFileBatchRequest {
350 file_ids: self.file_ids,
351 extra: self.extra,
352 })
353 }
354}
355
356#[derive(Clone, Debug, Serialize, PartialEq)]
359#[non_exhaustive]
360pub struct CreateVectorStoreSearchRequest {
361 pub query: String,
364 #[serde(skip_serializing_if = "Option::is_none")]
367 pub max_num_results: Option<u32>,
368 #[serde(rename = "filters", skip_serializing_if = "Option::is_none")]
371 pub filter: Option<Value>,
372 #[serde(flatten)]
375 pub extra: BTreeMap<String, Value>,
376}
377
378impl CreateVectorStoreSearchRequest {
379 pub fn builder() -> CreateVectorStoreSearchRequestBuilder {
382 CreateVectorStoreSearchRequestBuilder::default()
383 }
384}
385
386#[derive(Clone, Debug, Default)]
389#[non_exhaustive]
390pub struct CreateVectorStoreSearchRequestBuilder {
391 query: Option<String>,
392 max_num_results: Option<u32>,
393 filter: Option<Value>,
394 extra: BTreeMap<String, Value>,
395}
396
397impl CreateVectorStoreSearchRequestBuilder {
398 pub fn query(mut self, query: impl Into<String>) -> Self {
401 self.query = Some(query.into());
402 self
403 }
404
405 pub fn max_num_results(mut self, max_num_results: u32) -> Self {
408 self.max_num_results = Some(max_num_results);
409 self
410 }
411
412 pub fn filter(mut self, filter: Value) -> Self {
415 self.filter = Some(filter);
416 self
417 }
418
419 pub fn extra(mut self, name: impl Into<String>, value: Value) -> Self {
422 self.extra.insert(name.into(), value);
423 self
424 }
425
426 pub fn build(self) -> Result<CreateVectorStoreSearchRequest, LingerError> {
429 let query = required_string("query", self.query)?;
430 if self
431 .max_num_results
432 .is_some_and(|value| !(1..=50).contains(&value))
433 {
434 return Err(LingerError::invalid_config(
435 "max_num_results must be between 1 and 50",
436 ));
437 }
438 if self.filter.as_ref().is_some_and(Value::is_null) {
439 return Err(LingerError::invalid_config("filters must not be null"));
440 }
441 validate_extra_fields(&self.extra)?;
442 Ok(CreateVectorStoreSearchRequest {
443 query,
444 max_num_results: self.max_num_results,
445 filter: self.filter,
446 extra: self.extra,
447 })
448 }
449}
450
451#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
454#[non_exhaustive]
455pub struct VectorStore {
456 pub id: String,
459 pub object: String,
462 pub created_at: u64,
465 #[serde(default)]
468 pub name: Option<String>,
469 #[serde(default, alias = "usage_bytes")]
472 pub bytes: u64,
473 pub file_counts: VectorStoreFileCounts,
476 pub status: String,
479 #[serde(default)]
482 pub expires_after: Option<Value>,
483 #[serde(default)]
486 pub expires_at: Option<u64>,
487 #[serde(default)]
490 pub last_active_at: Option<u64>,
491 #[serde(default)]
494 pub metadata: BTreeMap<String, String>,
495 #[serde(flatten)]
498 pub extra: BTreeMap<String, Value>,
499 #[serde(skip)]
502 request_id: Option<RequestId>,
503}
504
505impl VectorStore {
506 pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
507 self.request_id = request_id;
508 self
509 }
510
511 pub fn request_id(&self) -> Option<&RequestId> {
514 self.request_id.as_ref()
515 }
516}
517
518#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
521#[non_exhaustive]
522pub struct VectorStoreFileCounts {
523 pub in_progress: u64,
526 pub completed: u64,
529 pub failed: u64,
532 pub cancelled: u64,
535 pub total: u64,
538}
539
540#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
543#[non_exhaustive]
544pub struct VectorStoreFile {
545 pub id: String,
548 pub object: String,
551 pub created_at: u64,
554 pub vector_store_id: String,
557 pub status: String,
560 #[serde(default)]
563 pub last_error: Option<Value>,
564 #[serde(default)]
567 pub usage_bytes: u64,
568 #[serde(default)]
571 pub chunking_strategy: Option<Value>,
572 #[serde(default)]
575 pub attributes: BTreeMap<String, Value>,
576 #[serde(flatten)]
579 pub extra: BTreeMap<String, Value>,
580 #[serde(skip)]
583 request_id: Option<RequestId>,
584}
585
586impl VectorStoreFile {
587 pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
588 self.request_id = request_id;
589 self
590 }
591
592 pub fn request_id(&self) -> Option<&RequestId> {
595 self.request_id.as_ref()
596 }
597}
598
599#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
602#[non_exhaustive]
603pub struct VectorStoreFilePage {
604 pub object: String,
607 #[serde(default)]
610 pub data: Vec<VectorStoreFile>,
611 #[serde(default)]
614 pub first_id: Option<String>,
615 #[serde(default)]
618 pub last_id: Option<String>,
619 pub has_more: bool,
622 #[serde(skip)]
625 request_id: Option<RequestId>,
626}
627
628impl VectorStoreFilePage {
629 pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
630 self.request_id = request_id;
631 self
632 }
633
634 pub fn request_id(&self) -> Option<&RequestId> {
637 self.request_id.as_ref()
638 }
639}
640
641#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
644#[non_exhaustive]
645pub struct VectorStoreFileContent {
646 #[serde(default)]
649 pub r#type: Option<String>,
650 #[serde(default)]
653 pub text: Option<String>,
654 #[serde(flatten)]
657 pub extra: BTreeMap<String, Value>,
658}
659
660#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
663#[non_exhaustive]
664pub struct VectorStoreFileContentPage {
665 pub object: String,
668 #[serde(default)]
671 pub data: Vec<VectorStoreFileContent>,
672 #[serde(default)]
675 pub has_more: bool,
676 #[serde(flatten)]
679 pub extra: BTreeMap<String, Value>,
680 #[serde(skip)]
683 request_id: Option<RequestId>,
684}
685
686impl VectorStoreFileContentPage {
687 pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
688 self.request_id = request_id;
689 self
690 }
691
692 pub fn request_id(&self) -> Option<&RequestId> {
695 self.request_id.as_ref()
696 }
697}
698
699#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
702#[non_exhaustive]
703pub struct VectorStoreSearchResult {
704 pub file_id: String,
707 #[serde(default)]
710 pub filename: Option<String>,
711 #[serde(default)]
714 pub score: Option<f64>,
715 #[serde(default)]
718 pub content: Vec<Value>,
719 #[serde(default)]
722 pub attributes: BTreeMap<String, Value>,
723 #[serde(flatten)]
726 pub extra: BTreeMap<String, Value>,
727}
728
729#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
732#[non_exhaustive]
733pub struct VectorStoreSearchPage {
734 pub object: String,
737 #[serde(default)]
740 pub data: Vec<VectorStoreSearchResult>,
741 pub has_more: bool,
744 #[serde(default)]
747 pub next_page: Option<String>,
748 #[serde(flatten)]
751 pub extra: BTreeMap<String, Value>,
752 #[serde(skip)]
755 request_id: Option<RequestId>,
756}
757
758impl VectorStoreSearchPage {
759 pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
760 self.request_id = request_id;
761 self
762 }
763
764 pub fn request_id(&self) -> Option<&RequestId> {
767 self.request_id.as_ref()
768 }
769}
770
771#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
774#[non_exhaustive]
775pub struct VectorStoreFileDeletion {
776 pub id: String,
779 pub object: String,
782 pub deleted: bool,
785 #[serde(skip)]
788 request_id: Option<RequestId>,
789}
790
791impl VectorStoreFileDeletion {
792 pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
793 self.request_id = request_id;
794 self
795 }
796
797 pub fn request_id(&self) -> Option<&RequestId> {
800 self.request_id.as_ref()
801 }
802}
803
804#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
807#[non_exhaustive]
808pub struct VectorStoreFileBatch {
809 pub id: String,
812 pub object: String,
815 pub created_at: u64,
818 pub vector_store_id: String,
821 pub status: String,
824 pub file_counts: VectorStoreFileCounts,
827 #[serde(flatten)]
830 pub extra: BTreeMap<String, Value>,
831 #[serde(skip)]
834 request_id: Option<RequestId>,
835}
836
837impl VectorStoreFileBatch {
838 pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
839 self.request_id = request_id;
840 self
841 }
842
843 pub fn request_id(&self) -> Option<&RequestId> {
846 self.request_id.as_ref()
847 }
848}
849
850#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
853#[non_exhaustive]
854pub struct VectorStorePage {
855 pub object: String,
858 #[serde(default)]
861 pub data: Vec<VectorStore>,
862 #[serde(default)]
865 pub first_id: Option<String>,
866 #[serde(default)]
869 pub last_id: Option<String>,
870 pub has_more: bool,
873 #[serde(skip)]
876 request_id: Option<RequestId>,
877}
878
879impl VectorStorePage {
880 pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
881 self.request_id = request_id;
882 self
883 }
884
885 pub fn request_id(&self) -> Option<&RequestId> {
888 self.request_id.as_ref()
889 }
890}
891
892#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
895#[non_exhaustive]
896pub struct VectorStoreDeletion {
897 pub id: String,
900 pub object: String,
903 pub deleted: bool,
906 #[serde(skip)]
909 request_id: Option<RequestId>,
910}
911
912impl VectorStoreDeletion {
913 pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
914 self.request_id = request_id;
915 self
916 }
917
918 pub fn request_id(&self) -> Option<&RequestId> {
921 self.request_id.as_ref()
922 }
923}
924
925fn validate_optional_string(name: &str, value: Option<&str>) -> Result<(), LingerError> {
926 if value.is_some_and(|value| value.trim().is_empty()) {
927 return Err(LingerError::invalid_config(format!(
928 "{name} must not be empty"
929 )));
930 }
931 Ok(())
932}
933
934fn validate_metadata(metadata: &BTreeMap<String, String>) -> Result<(), LingerError> {
935 for key in metadata.keys() {
936 if key.trim().is_empty() {
937 return Err(LingerError::invalid_config(
938 "metadata keys must not be empty",
939 ));
940 }
941 }
942 Ok(())
943}
944
945fn validate_optional_json_value(name: &str, value: Option<&Value>) -> Result<(), LingerError> {
946 if value.is_some_and(Value::is_null) {
947 return Err(LingerError::invalid_config(format!(
948 "{name} must not be null"
949 )));
950 }
951 Ok(())
952}
953
954fn validate_attributes(
955 attributes: &BTreeMap<String, Value>,
956 require_non_empty: bool,
957) -> Result<(), LingerError> {
958 if require_non_empty && attributes.is_empty() {
959 return Err(LingerError::invalid_config("attributes are required"));
960 }
961 for (key, value) in attributes {
962 if key.trim().is_empty() {
963 return Err(LingerError::invalid_config(
964 "attribute names must not be empty",
965 ));
966 }
967 if !(value.is_string() || value.is_number() || value.is_boolean()) {
968 return Err(LingerError::invalid_config(format!(
969 "attribute {key} must be a string, number, or boolean"
970 )));
971 }
972 }
973 Ok(())
974}
975
976fn required_string(name: &str, value: Option<String>) -> Result<String, LingerError> {
977 value
978 .filter(|value| !value.trim().is_empty())
979 .ok_or_else(|| LingerError::invalid_config(format!("{name} is required")))
980}
981
982fn validate_non_empty_values(name: &str, values: &[String]) -> Result<(), LingerError> {
983 if values.is_empty() {
984 return Err(LingerError::invalid_config(format!("{name} is required")));
985 }
986 if values.iter().any(|value| value.trim().is_empty()) {
987 return Err(LingerError::invalid_config(format!(
988 "{name} must not contain empty values"
989 )));
990 }
991 Ok(())
992}
993
994fn validate_extra_fields(extra: &BTreeMap<String, Value>) -> Result<(), LingerError> {
995 for (key, value) in extra {
996 if key.trim().is_empty() {
997 return Err(LingerError::invalid_config(
998 "extra field names must not be empty",
999 ));
1000 }
1001 if value.is_null() {
1002 return Err(LingerError::invalid_config(format!(
1003 "extra field {key} must not be null"
1004 )));
1005 }
1006 }
1007 Ok(())
1008}