chroma_types/
api_types.rs

1use crate::collection_configuration::InternalCollectionConfiguration;
2use crate::collection_configuration::InternalUpdateCollectionConfiguration;
3use crate::error::QueryConversionError;
4use crate::operator::GetResult;
5use crate::operator::Key;
6use crate::operator::KnnBatchResult;
7use crate::operator::KnnProjectionRecord;
8use crate::operator::ProjectionRecord;
9use crate::operator::SearchResult;
10use crate::plan::PlanToProtoError;
11use crate::plan::SearchPayload;
12use crate::validators::{
13    validate_metadata_vec, validate_name, validate_non_empty_collection_update_metadata,
14    validate_optional_metadata, validate_schema, validate_update_metadata_vec,
15};
16use crate::Collection;
17use crate::CollectionConfigurationToInternalConfigurationError;
18use crate::CollectionConversionError;
19use crate::CollectionUuid;
20use crate::DistributedSpannParametersFromSegmentError;
21use crate::EmbeddingsPayload;
22use crate::HnswParametersFromSegmentError;
23use crate::Metadata;
24use crate::RawWhereFields;
25use crate::Schema;
26use crate::SchemaError;
27use crate::SegmentConversionError;
28use crate::SegmentScopeConversionError;
29use crate::UpdateEmbeddingsPayload;
30use crate::UpdateMetadata;
31use crate::Where;
32use crate::WhereValidationError;
33use chroma_error::ChromaValidationError;
34use chroma_error::{ChromaError, ErrorCodes};
35use serde::Deserialize;
36use serde::Serialize;
37use std::time::SystemTimeError;
38use thiserror::Error;
39use tonic::Status;
40use uuid::Uuid;
41use validator::Validate;
42use validator::ValidationError;
43
44#[cfg(feature = "pyo3")]
45use pyo3::types::PyAnyMethods;
46
47#[derive(Debug, Error)]
48pub enum GetSegmentsError {
49    #[error("Could not parse segment")]
50    SegmentConversion(#[from] SegmentConversionError),
51    #[error("Unknown segment scope")]
52    UnknownScope(#[from] SegmentScopeConversionError),
53    #[error(transparent)]
54    Internal(#[from] Box<dyn ChromaError>),
55}
56
57impl ChromaError for GetSegmentsError {
58    fn code(&self) -> ErrorCodes {
59        match self {
60            GetSegmentsError::SegmentConversion(_) => ErrorCodes::Internal,
61            GetSegmentsError::UnknownScope(_) => ErrorCodes::Internal,
62            GetSegmentsError::Internal(err) => err.code(),
63        }
64    }
65}
66
67#[derive(Debug, Error)]
68pub enum GetCollectionWithSegmentsError {
69    #[error("Failed to convert proto collection")]
70    CollectionConversionError(#[from] CollectionConversionError),
71    #[error("Duplicate segment")]
72    DuplicateSegment,
73    #[error("Missing field: [{0}]")]
74    Field(String),
75    #[error("Failed to convert proto segment")]
76    SegmentConversionError(#[from] SegmentConversionError),
77    #[error("Failed to get segments")]
78    GetSegmentsError(#[from] GetSegmentsError),
79    #[error("Grpc error: {0}")]
80    Grpc(#[from] Status),
81    #[error("Collection [{0}] does not exist.")]
82    NotFound(String),
83    #[error(transparent)]
84    Internal(#[from] Box<dyn ChromaError>),
85}
86
87impl ChromaError for GetCollectionWithSegmentsError {
88    fn code(&self) -> ErrorCodes {
89        match self {
90            GetCollectionWithSegmentsError::CollectionConversionError(
91                collection_conversion_error,
92            ) => collection_conversion_error.code(),
93            GetCollectionWithSegmentsError::DuplicateSegment => ErrorCodes::Internal,
94            GetCollectionWithSegmentsError::Field(_) => ErrorCodes::FailedPrecondition,
95            GetCollectionWithSegmentsError::SegmentConversionError(segment_conversion_error) => {
96                segment_conversion_error.code()
97            }
98            GetCollectionWithSegmentsError::Grpc(status) => status.code().into(),
99            GetCollectionWithSegmentsError::GetSegmentsError(get_segments_error) => {
100                get_segments_error.code()
101            }
102            GetCollectionWithSegmentsError::NotFound(_) => ErrorCodes::NotFound,
103            GetCollectionWithSegmentsError::Internal(err) => err.code(),
104        }
105    }
106
107    fn should_trace_error(&self) -> bool {
108        if let Self::Grpc(status) = self {
109            status.code() != ErrorCodes::NotFound.into()
110        } else {
111            true
112        }
113    }
114}
115
116#[derive(Debug, Error)]
117pub enum BatchGetCollectionVersionFilePathsError {
118    #[error("Grpc error: {0}")]
119    Grpc(#[from] Status),
120    #[error("Could not parse UUID from string {1}: {0}")]
121    Uuid(uuid::Error, String),
122}
123
124impl ChromaError for BatchGetCollectionVersionFilePathsError {
125    fn code(&self) -> ErrorCodes {
126        match self {
127            BatchGetCollectionVersionFilePathsError::Grpc(status) => status.code().into(),
128            BatchGetCollectionVersionFilePathsError::Uuid(_, _) => ErrorCodes::InvalidArgument,
129        }
130    }
131}
132
133#[derive(Debug, Error)]
134pub enum BatchGetCollectionSoftDeleteStatusError {
135    #[error("Grpc error: {0}")]
136    Grpc(#[from] Status),
137    #[error("Could not parse UUID from string {1}: {0}")]
138    Uuid(uuid::Error, String),
139}
140
141impl ChromaError for BatchGetCollectionSoftDeleteStatusError {
142    fn code(&self) -> ErrorCodes {
143        match self {
144            BatchGetCollectionSoftDeleteStatusError::Grpc(status) => status.code().into(),
145            BatchGetCollectionSoftDeleteStatusError::Uuid(_, _) => ErrorCodes::InvalidArgument,
146        }
147    }
148}
149
150#[derive(Serialize)]
151#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
152pub struct ResetResponse {}
153
154#[derive(Debug, Error)]
155pub enum ResetError {
156    #[error(transparent)]
157    Cache(Box<dyn ChromaError>),
158    #[error(transparent)]
159    Internal(#[from] Box<dyn ChromaError>),
160    #[error("Reset is disabled by config")]
161    NotAllowed,
162}
163
164impl ChromaError for ResetError {
165    fn code(&self) -> ErrorCodes {
166        match self {
167            ResetError::Cache(err) => err.code(),
168            ResetError::Internal(err) => err.code(),
169            ResetError::NotAllowed => ErrorCodes::PermissionDenied,
170        }
171    }
172}
173
174#[derive(Serialize)]
175#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
176pub struct ChecklistResponse {
177    pub max_batch_size: u32,
178    pub supports_base64_encoding: bool,
179}
180
181#[derive(Debug, Error)]
182#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
183pub enum HeartbeatError {
184    #[error("system time error: {0}")]
185    CouldNotGetTime(String),
186}
187
188impl From<SystemTimeError> for HeartbeatError {
189    fn from(err: SystemTimeError) -> Self {
190        HeartbeatError::CouldNotGetTime(err.to_string())
191    }
192}
193
194impl ChromaError for HeartbeatError {
195    fn code(&self) -> ErrorCodes {
196        ErrorCodes::Internal
197    }
198}
199
200#[non_exhaustive]
201#[derive(Serialize, Validate, Deserialize)]
202#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
203pub struct CreateTenantRequest {
204    #[validate(length(min = 3))]
205    pub name: String,
206}
207
208impl CreateTenantRequest {
209    pub fn try_new(name: String) -> Result<Self, ChromaValidationError> {
210        let request = Self { name };
211        request.validate().map_err(ChromaValidationError::from)?;
212        Ok(request)
213    }
214}
215
216#[derive(Serialize, Deserialize)]
217#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
218pub struct CreateTenantResponse {}
219
220#[derive(Debug, Error)]
221pub enum CreateTenantError {
222    #[error("Tenant [{0}] already exists")]
223    AlreadyExists(String),
224    #[error(transparent)]
225    Internal(#[from] Box<dyn ChromaError>),
226}
227
228impl ChromaError for CreateTenantError {
229    fn code(&self) -> ErrorCodes {
230        match self {
231            CreateTenantError::AlreadyExists(_) => ErrorCodes::AlreadyExists,
232            CreateTenantError::Internal(err) => err.code(),
233        }
234    }
235}
236
237#[non_exhaustive]
238#[derive(Validate, Serialize)]
239#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
240pub struct GetTenantRequest {
241    pub name: String,
242}
243
244impl GetTenantRequest {
245    pub fn try_new(name: String) -> Result<Self, ChromaValidationError> {
246        let request = Self { name };
247        request.validate().map_err(ChromaValidationError::from)?;
248        Ok(request)
249    }
250}
251
252#[derive(Serialize)]
253#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
254#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
255pub struct GetTenantResponse {
256    pub name: String,
257    pub resource_name: Option<String>,
258}
259
260#[cfg(feature = "pyo3")]
261#[pyo3::pymethods]
262impl GetTenantResponse {
263    #[getter]
264    pub fn name(&self) -> &String {
265        &self.name
266    }
267
268    #[getter]
269    pub fn resource_name(&self) -> Option<String> {
270        self.resource_name.clone()
271    }
272}
273
274#[derive(Debug, Error)]
275pub enum GetTenantError {
276    #[error(transparent)]
277    Internal(#[from] Box<dyn ChromaError>),
278    #[error("Tenant [{0}] not found")]
279    NotFound(String),
280}
281
282impl ChromaError for GetTenantError {
283    fn code(&self) -> ErrorCodes {
284        match self {
285            GetTenantError::Internal(err) => err.code(),
286            GetTenantError::NotFound(_) => ErrorCodes::NotFound,
287        }
288    }
289}
290
291#[non_exhaustive]
292#[derive(Validate, Serialize)]
293#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
294pub struct UpdateTenantRequest {
295    pub tenant_id: String,
296    pub resource_name: String,
297}
298
299impl UpdateTenantRequest {
300    pub fn try_new(
301        tenant_id: String,
302        resource_name: String,
303    ) -> Result<Self, ChromaValidationError> {
304        let request = Self {
305            tenant_id,
306            resource_name,
307        };
308        request.validate().map_err(ChromaValidationError::from)?;
309        Ok(request)
310    }
311}
312
313#[derive(Serialize)]
314#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
315#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
316pub struct UpdateTenantResponse {}
317
318#[cfg(feature = "pyo3")]
319#[pyo3::pymethods]
320impl UpdateTenantResponse {}
321
322#[derive(Error, Debug)]
323pub enum UpdateTenantError {
324    #[error("Failed to set resource name")]
325    FailedToSetResourceName(#[from] tonic::Status),
326    #[error(transparent)]
327    Internal(#[from] Box<dyn ChromaError>),
328    #[error("Tenant [{0}] not found")]
329    NotFound(String),
330}
331
332impl ChromaError for UpdateTenantError {
333    fn code(&self) -> ErrorCodes {
334        match self {
335            UpdateTenantError::FailedToSetResourceName(_) => ErrorCodes::AlreadyExists,
336            UpdateTenantError::Internal(err) => err.code(),
337            UpdateTenantError::NotFound(_) => ErrorCodes::NotFound,
338        }
339    }
340}
341
342#[non_exhaustive]
343#[derive(Validate, Serialize)]
344#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
345pub struct CreateDatabaseRequest {
346    pub database_id: Uuid,
347    pub tenant_id: String,
348    #[validate(length(min = 3))]
349    pub database_name: String,
350}
351
352impl CreateDatabaseRequest {
353    pub fn try_new(
354        tenant_id: String,
355        database_name: String,
356    ) -> Result<Self, ChromaValidationError> {
357        let database_id = Uuid::new_v4();
358        let request = Self {
359            database_id,
360            tenant_id,
361            database_name,
362        };
363        request.validate().map_err(ChromaValidationError::from)?;
364        Ok(request)
365    }
366}
367
368#[derive(Serialize)]
369#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
370pub struct CreateDatabaseResponse {}
371
372#[derive(Error, Debug)]
373pub enum CreateDatabaseError {
374    #[error("Database [{0}] already exists")]
375    AlreadyExists(String),
376    #[error(transparent)]
377    Internal(#[from] Box<dyn ChromaError>),
378}
379
380impl ChromaError for CreateDatabaseError {
381    fn code(&self) -> ErrorCodes {
382        match self {
383            CreateDatabaseError::AlreadyExists(_) => ErrorCodes::AlreadyExists,
384            CreateDatabaseError::Internal(status) => status.code(),
385        }
386    }
387}
388
389#[derive(Serialize, Deserialize, Debug, Clone, Default)]
390#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
391#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
392pub struct Database {
393    pub id: Uuid,
394    pub name: String,
395    pub tenant: String,
396}
397
398#[cfg(feature = "pyo3")]
399#[pyo3::pymethods]
400impl Database {
401    #[getter]
402    fn id<'py>(&self, py: pyo3::Python<'py>) -> pyo3::PyResult<pyo3::Bound<'py, pyo3::PyAny>> {
403        let res = pyo3::prelude::PyModule::import(py, "uuid")?
404            .getattr("UUID")?
405            .call1((self.id.to_string(),))?;
406        Ok(res)
407    }
408
409    #[getter]
410    pub fn name(&self) -> &str {
411        &self.name
412    }
413
414    #[getter]
415    pub fn tenant(&self) -> &str {
416        &self.tenant
417    }
418}
419
420#[non_exhaustive]
421#[derive(Validate, Serialize)]
422#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
423pub struct ListDatabasesRequest {
424    pub tenant_id: String,
425    pub limit: Option<u32>,
426    pub offset: u32,
427}
428
429impl ListDatabasesRequest {
430    pub fn try_new(
431        tenant_id: String,
432        limit: Option<u32>,
433        offset: u32,
434    ) -> Result<Self, ChromaValidationError> {
435        let request = Self {
436            tenant_id,
437            limit,
438            offset,
439        };
440        request.validate().map_err(ChromaValidationError::from)?;
441        Ok(request)
442    }
443}
444
445pub type ListDatabasesResponse = Vec<Database>;
446
447#[derive(Debug, Error)]
448pub enum ListDatabasesError {
449    #[error(transparent)]
450    Internal(#[from] Box<dyn ChromaError>),
451    #[error("Invalid database id [{0}]")]
452    InvalidID(String),
453}
454
455impl ChromaError for ListDatabasesError {
456    fn code(&self) -> ErrorCodes {
457        match self {
458            ListDatabasesError::Internal(status) => status.code(),
459            ListDatabasesError::InvalidID(_) => ErrorCodes::InvalidArgument,
460        }
461    }
462}
463
464#[non_exhaustive]
465#[derive(Validate, Serialize)]
466#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
467pub struct GetDatabaseRequest {
468    pub tenant_id: String,
469    pub database_name: String,
470}
471
472impl GetDatabaseRequest {
473    pub fn try_new(
474        tenant_id: String,
475        database_name: String,
476    ) -> Result<Self, ChromaValidationError> {
477        let request = Self {
478            tenant_id,
479            database_name,
480        };
481        request.validate().map_err(ChromaValidationError::from)?;
482        Ok(request)
483    }
484}
485
486pub type GetDatabaseResponse = Database;
487
488#[derive(Error, Debug)]
489pub enum GetDatabaseError {
490    #[error(transparent)]
491    Internal(#[from] Box<dyn ChromaError>),
492    #[error("Invalid database id [{0}]")]
493    InvalidID(String),
494    #[error("Database [{0}] not found. Are you sure it exists?")]
495    NotFound(String),
496}
497
498impl ChromaError for GetDatabaseError {
499    fn code(&self) -> ErrorCodes {
500        match self {
501            GetDatabaseError::Internal(err) => err.code(),
502            GetDatabaseError::InvalidID(_) => ErrorCodes::InvalidArgument,
503            GetDatabaseError::NotFound(_) => ErrorCodes::NotFound,
504        }
505    }
506}
507
508#[non_exhaustive]
509#[derive(Validate, Serialize)]
510#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
511pub struct DeleteDatabaseRequest {
512    pub tenant_id: String,
513    pub database_name: String,
514}
515
516impl DeleteDatabaseRequest {
517    pub fn try_new(
518        tenant_id: String,
519        database_name: String,
520    ) -> Result<Self, ChromaValidationError> {
521        let request = Self {
522            tenant_id,
523            database_name,
524        };
525        request.validate().map_err(ChromaValidationError::from)?;
526        Ok(request)
527    }
528}
529
530#[derive(Serialize)]
531#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
532pub struct DeleteDatabaseResponse {}
533
534#[derive(Debug, Error)]
535pub enum DeleteDatabaseError {
536    #[error(transparent)]
537    Internal(#[from] Box<dyn ChromaError>),
538    #[error("Invalid database id [{0}]")]
539    InvalidID(String),
540    #[error("Database [{0}] not found")]
541    NotFound(String),
542}
543
544impl ChromaError for DeleteDatabaseError {
545    fn code(&self) -> ErrorCodes {
546        match self {
547            DeleteDatabaseError::Internal(err) => err.code(),
548            DeleteDatabaseError::InvalidID(_) => ErrorCodes::InvalidArgument,
549            DeleteDatabaseError::NotFound(_) => ErrorCodes::NotFound,
550        }
551    }
552}
553
554#[derive(Debug, Error)]
555pub enum FinishDatabaseDeletionError {
556    #[error(transparent)]
557    Internal(#[from] Box<dyn ChromaError>),
558}
559
560impl ChromaError for FinishDatabaseDeletionError {
561    fn code(&self) -> ErrorCodes {
562        match self {
563            FinishDatabaseDeletionError::Internal(err) => err.code(),
564        }
565    }
566}
567
568#[non_exhaustive]
569#[derive(Validate, Debug, Serialize)]
570#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
571pub struct ListCollectionsRequest {
572    pub tenant_id: String,
573    pub database_name: String,
574    pub limit: Option<u32>,
575    pub offset: u32,
576}
577
578impl ListCollectionsRequest {
579    pub fn try_new(
580        tenant_id: String,
581        database_name: String,
582        limit: Option<u32>,
583        offset: u32,
584    ) -> Result<Self, ChromaValidationError> {
585        let request = Self {
586            tenant_id,
587            database_name,
588            limit,
589            offset,
590        };
591        request.validate().map_err(ChromaValidationError::from)?;
592        Ok(request)
593    }
594}
595
596pub type ListCollectionsResponse = Vec<Collection>;
597
598#[non_exhaustive]
599#[derive(Validate, Serialize)]
600#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
601pub struct CountCollectionsRequest {
602    pub tenant_id: String,
603    pub database_name: String,
604}
605
606impl CountCollectionsRequest {
607    pub fn try_new(
608        tenant_id: String,
609        database_name: String,
610    ) -> Result<Self, ChromaValidationError> {
611        let request = Self {
612            tenant_id,
613            database_name,
614        };
615        request.validate().map_err(ChromaValidationError::from)?;
616        Ok(request)
617    }
618}
619
620pub type CountCollectionsResponse = u32;
621
622#[non_exhaustive]
623#[derive(Validate, Clone, Serialize)]
624#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
625pub struct GetCollectionRequest {
626    pub tenant_id: String,
627    pub database_name: String,
628    pub collection_name: String,
629}
630
631impl GetCollectionRequest {
632    pub fn try_new(
633        tenant_id: String,
634        database_name: String,
635        collection_name: String,
636    ) -> Result<Self, ChromaValidationError> {
637        let request = Self {
638            tenant_id,
639            database_name,
640            collection_name,
641        };
642        request.validate().map_err(ChromaValidationError::from)?;
643        Ok(request)
644    }
645}
646
647pub type GetCollectionResponse = Collection;
648
649#[derive(Debug, Error)]
650pub enum GetCollectionError {
651    #[error("Failed to reconcile schema: {0}")]
652    InvalidSchema(#[from] SchemaError),
653    #[error(transparent)]
654    Internal(#[from] Box<dyn ChromaError>),
655    #[error("Collection [{0}] does not exist")]
656    NotFound(String),
657}
658
659impl ChromaError for GetCollectionError {
660    fn code(&self) -> ErrorCodes {
661        match self {
662            GetCollectionError::InvalidSchema(e) => e.code(),
663            GetCollectionError::Internal(err) => err.code(),
664            GetCollectionError::NotFound(_) => ErrorCodes::NotFound,
665        }
666    }
667}
668
669#[non_exhaustive]
670#[derive(Clone, Debug, Validate, Serialize)]
671#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
672pub struct CreateCollectionRequest {
673    pub tenant_id: String,
674    pub database_name: String,
675    #[validate(custom(function = "validate_name"))]
676    pub name: String,
677    #[validate(custom(function = "validate_optional_metadata"))]
678    pub metadata: Option<Metadata>,
679    pub configuration: Option<InternalCollectionConfiguration>,
680    #[validate(custom(function = "validate_schema"))]
681    pub schema: Option<Schema>,
682    pub get_or_create: bool,
683}
684
685impl CreateCollectionRequest {
686    pub fn try_new(
687        tenant_id: String,
688        database_name: String,
689        name: String,
690        metadata: Option<Metadata>,
691        configuration: Option<InternalCollectionConfiguration>,
692        schema: Option<Schema>,
693        get_or_create: bool,
694    ) -> Result<Self, ChromaValidationError> {
695        let request = Self {
696            tenant_id,
697            database_name,
698            name,
699            metadata,
700            configuration,
701            schema,
702            get_or_create,
703        };
704        request.validate().map_err(ChromaValidationError::from)?;
705        Ok(request)
706    }
707}
708
709pub type CreateCollectionResponse = Collection;
710
711#[derive(Debug, Error)]
712pub enum CreateCollectionError {
713    #[error("Invalid HNSW parameters: {0}")]
714    InvalidHnswParameters(#[from] HnswParametersFromSegmentError),
715    #[error("Could not parse config: {0}")]
716    InvalidConfig(#[from] CollectionConfigurationToInternalConfigurationError),
717    #[error("Invalid Spann parameters: {0}")]
718    InvalidSpannParameters(#[from] DistributedSpannParametersFromSegmentError),
719    #[error("Collection [{0}] already exists")]
720    AlreadyExists(String),
721    #[error("Database [{0}] does not exist")]
722    DatabaseNotFound(String),
723    #[error("Could not fetch collections: {0}")]
724    Get(#[from] GetCollectionsError),
725    #[error("Could not deserialize configuration: {0}")]
726    Configuration(serde_json::Error),
727    #[error("Could not serialize schema: {0}")]
728    Schema(#[source] SchemaError),
729    #[error(transparent)]
730    Internal(#[from] Box<dyn ChromaError>),
731    #[error("The operation was aborted, {0}")]
732    Aborted(String),
733    #[error("SPANN is still in development. Not allowed to created spann indexes")]
734    SpannNotImplemented,
735    #[error("HNSW is not supported on this platform")]
736    HnswNotSupported,
737    #[error("Failed to parse db id")]
738    DatabaseIdParseError,
739    #[error("Failed to reconcile schema: {0}")]
740    InvalidSchema(#[source] SchemaError),
741}
742
743impl ChromaError for CreateCollectionError {
744    fn code(&self) -> ErrorCodes {
745        match self {
746            CreateCollectionError::InvalidHnswParameters(_) => ErrorCodes::InvalidArgument,
747            CreateCollectionError::InvalidConfig(_) => ErrorCodes::InvalidArgument,
748            CreateCollectionError::InvalidSpannParameters(_) => ErrorCodes::InvalidArgument,
749            CreateCollectionError::AlreadyExists(_) => ErrorCodes::AlreadyExists,
750            CreateCollectionError::DatabaseNotFound(_) => ErrorCodes::InvalidArgument,
751            CreateCollectionError::Get(err) => err.code(),
752            CreateCollectionError::Configuration(_) => ErrorCodes::Internal,
753            CreateCollectionError::Internal(err) => err.code(),
754            CreateCollectionError::Aborted(_) => ErrorCodes::Aborted,
755            CreateCollectionError::SpannNotImplemented => ErrorCodes::InvalidArgument,
756            CreateCollectionError::HnswNotSupported => ErrorCodes::InvalidArgument,
757            CreateCollectionError::DatabaseIdParseError => ErrorCodes::Internal,
758            CreateCollectionError::InvalidSchema(e) => e.code(),
759            CreateCollectionError::Schema(e) => e.code(),
760        }
761    }
762}
763
764#[derive(Debug, Error)]
765pub enum CountCollectionsError {
766    #[error("Internal error in getting count")]
767    Internal,
768}
769
770impl ChromaError for CountCollectionsError {
771    fn code(&self) -> ErrorCodes {
772        match self {
773            CountCollectionsError::Internal => ErrorCodes::Internal,
774        }
775    }
776}
777
778#[derive(Debug, Error)]
779pub enum GetCollectionsError {
780    #[error("Failed to reconcile schema: {0}")]
781    InvalidSchema(#[from] SchemaError),
782    #[error(transparent)]
783    Internal(#[from] Box<dyn ChromaError>),
784    #[error("Could not deserialize configuration")]
785    Configuration(#[source] serde_json::Error),
786    #[error("Could not deserialize collection ID")]
787    CollectionId(#[from] uuid::Error),
788    #[error("Could not deserialize database ID")]
789    DatabaseId,
790    #[error("Could not deserialize schema")]
791    Schema(#[source] serde_json::Error),
792}
793
794impl ChromaError for GetCollectionsError {
795    fn code(&self) -> ErrorCodes {
796        match self {
797            GetCollectionsError::InvalidSchema(e) => e.code(),
798            GetCollectionsError::Internal(err) => err.code(),
799            GetCollectionsError::Configuration(_) => ErrorCodes::Internal,
800            GetCollectionsError::CollectionId(_) => ErrorCodes::Internal,
801            GetCollectionsError::DatabaseId => ErrorCodes::Internal,
802            GetCollectionsError::Schema(_) => ErrorCodes::Internal,
803        }
804    }
805}
806
807#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
808#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
809pub struct ChromaResourceName {
810    pub tenant_resource_name: String,
811    pub database_name: String,
812    pub collection_name: String,
813}
814#[non_exhaustive]
815#[derive(Clone, Serialize)]
816#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
817pub struct GetCollectionByCrnRequest {
818    pub parsed_crn: ChromaResourceName,
819}
820
821impl GetCollectionByCrnRequest {
822    pub fn try_new(crn: String) -> Result<Self, ChromaValidationError> {
823        let parsed_crn = parse_and_validate_crn(&crn)?;
824        Ok(Self { parsed_crn })
825    }
826}
827
828fn parse_and_validate_crn(crn: &str) -> Result<ChromaResourceName, ChromaValidationError> {
829    let mut parts = crn.splitn(4, ':');
830    if let (Some(p1), Some(p2), Some(p3), None) =
831        (parts.next(), parts.next(), parts.next(), parts.next())
832    {
833        if !p1.is_empty() && !p2.is_empty() && !p3.is_empty() {
834            return Ok(ChromaResourceName {
835                tenant_resource_name: p1.to_string(),
836                database_name: p2.to_string(),
837                collection_name: p3.to_string(),
838            });
839        }
840    }
841    let mut err = ValidationError::new("invalid_crn_format");
842    err.message = Some(
843        "CRN must be in the format <tenant_resource_name>:<database_name>:<collection_name> with non-empty parts"
844            .into(),
845    );
846    Err(ChromaValidationError::from(("crn", err)))
847}
848
849pub type GetCollectionByCrnResponse = Collection;
850
851#[derive(Debug, Error)]
852pub enum GetCollectionByCrnError {
853    #[error("Failed to reconcile schema: {0}")]
854    InvalidSchema(#[from] SchemaError),
855    #[error(transparent)]
856    Internal(#[from] Box<dyn ChromaError>),
857    #[error("Collection [{0}] does not exist")]
858    NotFound(String),
859}
860
861impl ChromaError for GetCollectionByCrnError {
862    fn code(&self) -> ErrorCodes {
863        match self {
864            GetCollectionByCrnError::InvalidSchema(e) => e.code(),
865            GetCollectionByCrnError::Internal(err) => err.code(),
866            GetCollectionByCrnError::NotFound(_) => ErrorCodes::NotFound,
867        }
868    }
869}
870
871#[derive(Clone, Deserialize, Serialize, Debug)]
872#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
873pub enum CollectionMetadataUpdate {
874    ResetMetadata,
875    UpdateMetadata(UpdateMetadata),
876}
877
878#[non_exhaustive]
879#[derive(Clone, Validate, Debug, Serialize)]
880#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
881pub struct UpdateCollectionRequest {
882    pub collection_id: CollectionUuid,
883    #[validate(custom(function = "validate_name"))]
884    pub new_name: Option<String>,
885    #[validate(custom(function = "validate_non_empty_collection_update_metadata"))]
886    pub new_metadata: Option<CollectionMetadataUpdate>,
887    pub new_configuration: Option<InternalUpdateCollectionConfiguration>,
888}
889
890impl UpdateCollectionRequest {
891    pub fn try_new(
892        collection_id: CollectionUuid,
893        new_name: Option<String>,
894        new_metadata: Option<CollectionMetadataUpdate>,
895        new_configuration: Option<InternalUpdateCollectionConfiguration>,
896    ) -> Result<Self, ChromaValidationError> {
897        let request = Self {
898            collection_id,
899            new_name,
900            new_metadata,
901            new_configuration,
902        };
903        request.validate().map_err(ChromaValidationError::from)?;
904        Ok(request)
905    }
906}
907
908#[derive(Serialize)]
909#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
910pub struct UpdateCollectionResponse {}
911
912#[derive(Error, Debug)]
913pub enum UpdateCollectionError {
914    #[error("Collection [{0}] does not exist")]
915    NotFound(String),
916    #[error("Metadata reset unsupported")]
917    MetadataResetUnsupported,
918    #[error("Could not serialize configuration")]
919    Configuration(#[source] serde_json::Error),
920    #[error(transparent)]
921    Internal(#[from] Box<dyn ChromaError>),
922    #[error("Could not parse config: {0}")]
923    InvalidConfig(#[from] CollectionConfigurationToInternalConfigurationError),
924    #[error("SPANN is still in development. Not allowed to created spann indexes")]
925    SpannNotImplemented,
926    #[error("Could not serialize schema: {0}")]
927    Schema(#[source] serde_json::Error),
928}
929
930impl ChromaError for UpdateCollectionError {
931    fn code(&self) -> ErrorCodes {
932        match self {
933            UpdateCollectionError::NotFound(_) => ErrorCodes::NotFound,
934            UpdateCollectionError::MetadataResetUnsupported => ErrorCodes::InvalidArgument,
935            UpdateCollectionError::Configuration(_) => ErrorCodes::Internal,
936            UpdateCollectionError::Internal(err) => err.code(),
937            UpdateCollectionError::InvalidConfig(_) => ErrorCodes::InvalidArgument,
938            UpdateCollectionError::SpannNotImplemented => ErrorCodes::InvalidArgument,
939            UpdateCollectionError::Schema(_) => ErrorCodes::Internal,
940        }
941    }
942}
943
944#[non_exhaustive]
945#[derive(Clone, Validate, Serialize)]
946#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
947pub struct DeleteCollectionRequest {
948    pub tenant_id: String,
949    pub database_name: String,
950    pub collection_name: String,
951}
952
953impl DeleteCollectionRequest {
954    pub fn try_new(
955        tenant_id: String,
956        database_name: String,
957        collection_name: String,
958    ) -> Result<Self, ChromaValidationError> {
959        let request = Self {
960            tenant_id,
961            database_name,
962            collection_name,
963        };
964        request.validate().map_err(ChromaValidationError::from)?;
965        Ok(request)
966    }
967}
968
969#[derive(Serialize)]
970#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
971pub struct DeleteCollectionResponse {}
972
973#[derive(Error, Debug)]
974pub enum DeleteCollectionError {
975    #[error("Collection [{0}] does not exist")]
976    NotFound(String),
977    #[error(transparent)]
978    Validation(#[from] ChromaValidationError),
979    #[error(transparent)]
980    Get(#[from] GetCollectionError),
981    #[error(transparent)]
982    Internal(#[from] Box<dyn ChromaError>),
983}
984
985impl ChromaError for DeleteCollectionError {
986    fn code(&self) -> ErrorCodes {
987        match self {
988            DeleteCollectionError::Validation(err) => err.code(),
989            DeleteCollectionError::NotFound(_) => ErrorCodes::NotFound,
990            DeleteCollectionError::Get(err) => err.code(),
991            DeleteCollectionError::Internal(err) => err.code(),
992        }
993    }
994}
995
996#[non_exhaustive]
997#[derive(Clone, Validate, Serialize)]
998#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
999pub struct ForkCollectionRequest {
1000    pub tenant_id: String,
1001    pub database_name: String,
1002    pub source_collection_id: CollectionUuid,
1003    pub target_collection_name: String,
1004}
1005
1006impl ForkCollectionRequest {
1007    pub fn try_new(
1008        tenant_id: String,
1009        database_name: String,
1010        source_collection_id: CollectionUuid,
1011        target_collection_name: String,
1012    ) -> Result<Self, ChromaValidationError> {
1013        let request = Self {
1014            tenant_id,
1015            database_name,
1016            source_collection_id,
1017            target_collection_name,
1018        };
1019        request.validate().map_err(ChromaValidationError::from)?;
1020        Ok(request)
1021    }
1022}
1023
1024pub type ForkCollectionResponse = Collection;
1025
1026#[derive(Clone, Debug)]
1027pub struct ForkLogsResponse {
1028    pub compaction_offset: u64,
1029    pub enumeration_offset: u64,
1030}
1031
1032#[derive(Error, Debug)]
1033pub enum ForkCollectionError {
1034    #[error("Collection [{0}] already exists")]
1035    AlreadyExists(String),
1036    #[error("Failed to convert proto collection")]
1037    CollectionConversionError(#[from] CollectionConversionError),
1038    #[error("Duplicate segment")]
1039    DuplicateSegment,
1040    #[error("Missing field: [{0}]")]
1041    Field(String),
1042    #[error("Collection forking is unsupported for local chroma")]
1043    Local,
1044    #[error(transparent)]
1045    Internal(#[from] Box<dyn ChromaError>),
1046    #[error("Collection [{0}] does not exist")]
1047    NotFound(String),
1048    #[error("Failed to convert proto segment")]
1049    SegmentConversionError(#[from] SegmentConversionError),
1050    #[error("Failed to reconcile schema: {0}")]
1051    InvalidSchema(#[from] SchemaError),
1052}
1053
1054impl ChromaError for ForkCollectionError {
1055    fn code(&self) -> ErrorCodes {
1056        match self {
1057            ForkCollectionError::NotFound(_) => ErrorCodes::NotFound,
1058            ForkCollectionError::AlreadyExists(_) => ErrorCodes::AlreadyExists,
1059            ForkCollectionError::CollectionConversionError(e) => e.code(),
1060            ForkCollectionError::DuplicateSegment => ErrorCodes::Internal,
1061            ForkCollectionError::Field(_) => ErrorCodes::FailedPrecondition,
1062            ForkCollectionError::Local => ErrorCodes::Unimplemented,
1063            ForkCollectionError::Internal(e) => e.code(),
1064            ForkCollectionError::SegmentConversionError(e) => e.code(),
1065            ForkCollectionError::InvalidSchema(e) => e.code(),
1066        }
1067    }
1068}
1069
1070#[derive(Debug, Error)]
1071pub enum CountForksError {
1072    #[error("Collection [{0}] does not exist")]
1073    NotFound(String),
1074    #[error(transparent)]
1075    Internal(#[from] Box<dyn ChromaError>),
1076    #[error("Count forks is unsupported for local chroma")]
1077    Local,
1078}
1079
1080impl ChromaError for CountForksError {
1081    fn code(&self) -> ErrorCodes {
1082        match self {
1083            CountForksError::NotFound(_) => ErrorCodes::NotFound,
1084            CountForksError::Internal(chroma_error) => chroma_error.code(),
1085            CountForksError::Local => ErrorCodes::Unimplemented,
1086        }
1087    }
1088}
1089
1090#[derive(Debug, Error)]
1091pub enum ListAttachedFunctionsError {
1092    #[error("Collection [{0}] does not exist")]
1093    NotFound(String),
1094    #[error(transparent)]
1095    Internal(#[from] Box<dyn ChromaError>),
1096    #[error("List attached functions is not implemented")]
1097    NotImplemented,
1098}
1099
1100impl ChromaError for ListAttachedFunctionsError {
1101    fn code(&self) -> ErrorCodes {
1102        match self {
1103            ListAttachedFunctionsError::NotFound(_) => ErrorCodes::NotFound,
1104            ListAttachedFunctionsError::Internal(chroma_error) => chroma_error.code(),
1105            ListAttachedFunctionsError::NotImplemented => ErrorCodes::Unimplemented,
1106        }
1107    }
1108}
1109
1110#[derive(Debug, Error)]
1111pub enum GetCollectionSizeError {
1112    #[error(transparent)]
1113    Internal(#[from] Box<dyn ChromaError>),
1114    #[error("Collection [{0}] does not exist")]
1115    NotFound(String),
1116}
1117
1118impl ChromaError for GetCollectionSizeError {
1119    fn code(&self) -> ErrorCodes {
1120        match self {
1121            GetCollectionSizeError::Internal(err) => err.code(),
1122            GetCollectionSizeError::NotFound(_) => ErrorCodes::NotFound,
1123        }
1124    }
1125}
1126
1127#[derive(Error, Debug)]
1128pub enum ListCollectionVersionsError {
1129    #[error(transparent)]
1130    Internal(#[from] Box<dyn ChromaError>),
1131    #[error("Collection [{0}] does not exist")]
1132    NotFound(String),
1133}
1134
1135impl ChromaError for ListCollectionVersionsError {
1136    fn code(&self) -> ErrorCodes {
1137        match self {
1138            ListCollectionVersionsError::Internal(err) => err.code(),
1139            ListCollectionVersionsError::NotFound(_) => ErrorCodes::NotFound,
1140        }
1141    }
1142}
1143
1144////////////////////////// Metadata Key Constants //////////////////////////
1145
1146pub const CHROMA_KEY: &str = "chroma:";
1147pub const CHROMA_DOCUMENT_KEY: &str = "chroma:document";
1148pub const CHROMA_URI_KEY: &str = "chroma:uri";
1149
1150////////////////////////// AddCollectionRecords //////////////////////////
1151
1152#[derive(Serialize, Deserialize, Debug, Clone)]
1153#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1154pub struct AddCollectionRecordsPayload {
1155    pub ids: Vec<String>,
1156    pub embeddings: EmbeddingsPayload,
1157    pub documents: Option<Vec<Option<String>>>,
1158    pub uris: Option<Vec<Option<String>>>,
1159    pub metadatas: Option<Vec<Option<Metadata>>>,
1160}
1161
1162impl AddCollectionRecordsPayload {
1163    pub fn new(
1164        ids: Vec<String>,
1165        embeddings: Vec<Vec<f32>>,
1166        documents: Option<Vec<Option<String>>>,
1167        uris: Option<Vec<Option<String>>>,
1168        metadatas: Option<Vec<Option<Metadata>>>,
1169    ) -> Self {
1170        Self {
1171            ids,
1172            embeddings: EmbeddingsPayload::JsonArrays(embeddings),
1173            documents,
1174            uris,
1175            metadatas,
1176        }
1177    }
1178}
1179
1180#[non_exhaustive]
1181#[derive(Debug, Clone, Validate, Serialize)]
1182#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1183pub struct AddCollectionRecordsRequest {
1184    pub tenant_id: String,
1185    pub database_name: String,
1186    pub collection_id: CollectionUuid,
1187    pub ids: Vec<String>,
1188    #[validate(custom(function = "validate_embeddings"))]
1189    pub embeddings: Vec<Vec<f32>>,
1190    pub documents: Option<Vec<Option<String>>>,
1191    pub uris: Option<Vec<Option<String>>>,
1192    #[validate(custom(function = "validate_metadata_vec"))]
1193    pub metadatas: Option<Vec<Option<Metadata>>>,
1194}
1195
1196impl AddCollectionRecordsRequest {
1197    #[allow(clippy::too_many_arguments)]
1198    pub fn try_new(
1199        tenant_id: String,
1200        database_name: String,
1201        collection_id: CollectionUuid,
1202        ids: Vec<String>,
1203        embeddings: Vec<Vec<f32>>,
1204        documents: Option<Vec<Option<String>>>,
1205        uris: Option<Vec<Option<String>>>,
1206        metadatas: Option<Vec<Option<Metadata>>>,
1207    ) -> Result<Self, ChromaValidationError> {
1208        let request = Self {
1209            tenant_id,
1210            database_name,
1211            collection_id,
1212            ids,
1213            embeddings,
1214            documents,
1215            uris,
1216            metadatas,
1217        };
1218        request.validate().map_err(ChromaValidationError::from)?;
1219        Ok(request)
1220    }
1221
1222    pub fn into_payload(self) -> AddCollectionRecordsPayload {
1223        AddCollectionRecordsPayload {
1224            ids: self.ids,
1225            embeddings: EmbeddingsPayload::JsonArrays(self.embeddings),
1226            documents: self.documents,
1227            uris: self.uris,
1228            metadatas: self.metadatas,
1229        }
1230    }
1231}
1232
1233fn validate_embeddings(embeddings: &[Vec<f32>]) -> Result<(), ValidationError> {
1234    if embeddings.iter().any(|e| e.is_empty()) {
1235        return Err(ValidationError::new("embedding_minimum_dimensions")
1236            .with_message("Each embedding must have at least 1 dimension".into()));
1237    }
1238    Ok(())
1239}
1240
1241#[derive(Serialize, Default, Deserialize)]
1242#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1243pub struct AddCollectionRecordsResponse {}
1244
1245#[derive(Error, Debug)]
1246pub enum AddCollectionRecordsError {
1247    #[error("Failed to get collection: {0}")]
1248    Collection(#[from] GetCollectionError),
1249    #[error("Backoff and retry")]
1250    Backoff,
1251    #[error(transparent)]
1252    Other(#[from] Box<dyn ChromaError>),
1253}
1254
1255impl ChromaError for AddCollectionRecordsError {
1256    fn code(&self) -> ErrorCodes {
1257        match self {
1258            AddCollectionRecordsError::Collection(err) => err.code(),
1259            AddCollectionRecordsError::Backoff => ErrorCodes::ResourceExhausted,
1260            AddCollectionRecordsError::Other(err) => err.code(),
1261        }
1262    }
1263}
1264
1265////////////////////////// UpdateCollectionRecords //////////////////////////
1266
1267#[derive(Deserialize, Debug, Clone, Serialize)]
1268#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1269pub struct UpdateCollectionRecordsPayload {
1270    pub ids: Vec<String>,
1271    pub embeddings: Option<UpdateEmbeddingsPayload>,
1272    pub documents: Option<Vec<Option<String>>>,
1273    pub uris: Option<Vec<Option<String>>>,
1274    pub metadatas: Option<Vec<Option<UpdateMetadata>>>,
1275}
1276
1277#[non_exhaustive]
1278#[derive(Debug, Clone, Validate, Serialize)]
1279#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1280pub struct UpdateCollectionRecordsRequest {
1281    pub tenant_id: String,
1282    pub database_name: String,
1283    pub collection_id: CollectionUuid,
1284    pub ids: Vec<String>,
1285    pub embeddings: Option<Vec<Option<Vec<f32>>>>,
1286    pub documents: Option<Vec<Option<String>>>,
1287    pub uris: Option<Vec<Option<String>>>,
1288    #[validate(custom(function = "validate_update_metadata_vec"))]
1289    pub metadatas: Option<Vec<Option<UpdateMetadata>>>,
1290}
1291
1292impl UpdateCollectionRecordsRequest {
1293    #[allow(clippy::too_many_arguments)]
1294    pub fn try_new(
1295        tenant_id: String,
1296        database_name: String,
1297        collection_id: CollectionUuid,
1298        ids: Vec<String>,
1299        embeddings: Option<Vec<Option<Vec<f32>>>>,
1300        documents: Option<Vec<Option<String>>>,
1301        uris: Option<Vec<Option<String>>>,
1302        metadatas: Option<Vec<Option<UpdateMetadata>>>,
1303    ) -> Result<Self, ChromaValidationError> {
1304        let request = Self {
1305            tenant_id,
1306            database_name,
1307            collection_id,
1308            ids,
1309            embeddings,
1310            documents,
1311            uris,
1312            metadatas,
1313        };
1314        request.validate().map_err(ChromaValidationError::from)?;
1315        Ok(request)
1316    }
1317
1318    pub fn into_payload(self) -> UpdateCollectionRecordsPayload {
1319        UpdateCollectionRecordsPayload {
1320            ids: self.ids,
1321            embeddings: self.embeddings.map(UpdateEmbeddingsPayload::JsonArrays),
1322            documents: self.documents,
1323            uris: self.uris,
1324            metadatas: self.metadatas,
1325        }
1326    }
1327}
1328
1329#[derive(Serialize, Deserialize)]
1330#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1331pub struct UpdateCollectionRecordsResponse {}
1332
1333#[derive(Error, Debug)]
1334pub enum UpdateCollectionRecordsError {
1335    #[error("Backoff and retry")]
1336    Backoff,
1337    #[error(transparent)]
1338    Other(#[from] Box<dyn ChromaError>),
1339}
1340
1341impl ChromaError for UpdateCollectionRecordsError {
1342    fn code(&self) -> ErrorCodes {
1343        match self {
1344            UpdateCollectionRecordsError::Backoff => ErrorCodes::ResourceExhausted,
1345            UpdateCollectionRecordsError::Other(err) => err.code(),
1346        }
1347    }
1348}
1349
1350////////////////////////// UpsertCollectionRecords //////////////////////////
1351
1352#[derive(Deserialize, Debug, Clone, Serialize)]
1353#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1354pub struct UpsertCollectionRecordsPayload {
1355    pub ids: Vec<String>,
1356    pub embeddings: EmbeddingsPayload,
1357    pub documents: Option<Vec<Option<String>>>,
1358    pub uris: Option<Vec<Option<String>>>,
1359    pub metadatas: Option<Vec<Option<UpdateMetadata>>>,
1360}
1361
1362#[non_exhaustive]
1363#[derive(Debug, Clone, Validate, Serialize)]
1364#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1365pub struct UpsertCollectionRecordsRequest {
1366    pub tenant_id: String,
1367    pub database_name: String,
1368    pub collection_id: CollectionUuid,
1369    pub ids: Vec<String>,
1370    #[validate(custom(function = "validate_embeddings"))]
1371    pub embeddings: Vec<Vec<f32>>,
1372    pub documents: Option<Vec<Option<String>>>,
1373    pub uris: Option<Vec<Option<String>>>,
1374    #[validate(custom(function = "validate_update_metadata_vec"))]
1375    pub metadatas: Option<Vec<Option<UpdateMetadata>>>,
1376}
1377
1378impl UpsertCollectionRecordsRequest {
1379    #[allow(clippy::too_many_arguments)]
1380    pub fn try_new(
1381        tenant_id: String,
1382        database_name: String,
1383        collection_id: CollectionUuid,
1384        ids: Vec<String>,
1385        embeddings: Vec<Vec<f32>>,
1386        documents: Option<Vec<Option<String>>>,
1387        uris: Option<Vec<Option<String>>>,
1388        metadatas: Option<Vec<Option<UpdateMetadata>>>,
1389    ) -> Result<Self, ChromaValidationError> {
1390        let request = Self {
1391            tenant_id,
1392            database_name,
1393            collection_id,
1394            ids,
1395            embeddings,
1396            documents,
1397            uris,
1398            metadatas,
1399        };
1400        request.validate().map_err(ChromaValidationError::from)?;
1401        Ok(request)
1402    }
1403
1404    pub fn into_payload(self) -> UpsertCollectionRecordsPayload {
1405        UpsertCollectionRecordsPayload {
1406            ids: self.ids.clone(),
1407            embeddings: EmbeddingsPayload::JsonArrays(self.embeddings.clone()),
1408            documents: self.documents.clone(),
1409            uris: self.uris.clone(),
1410            metadatas: self.metadatas.clone(),
1411        }
1412    }
1413}
1414
1415#[derive(Serialize, Deserialize)]
1416#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1417pub struct UpsertCollectionRecordsResponse {}
1418
1419#[derive(Error, Debug)]
1420pub enum UpsertCollectionRecordsError {
1421    #[error("Backoff and retry")]
1422    Backoff,
1423    #[error(transparent)]
1424    Other(#[from] Box<dyn ChromaError>),
1425}
1426
1427impl ChromaError for UpsertCollectionRecordsError {
1428    fn code(&self) -> ErrorCodes {
1429        match self {
1430            UpsertCollectionRecordsError::Backoff => ErrorCodes::ResourceExhausted,
1431            UpsertCollectionRecordsError::Other(err) => err.code(),
1432        }
1433    }
1434}
1435
1436////////////////////////// DeleteCollectionRecords //////////////////////////
1437
1438#[derive(Deserialize, Debug, Clone, Serialize)]
1439#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1440pub struct DeleteCollectionRecordsPayload {
1441    pub ids: Option<Vec<String>>,
1442    #[serde(flatten)]
1443    pub where_fields: RawWhereFields,
1444}
1445
1446#[non_exhaustive]
1447#[derive(Debug, Clone, Validate, Serialize)]
1448#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1449pub struct DeleteCollectionRecordsRequest {
1450    pub tenant_id: String,
1451    pub database_name: String,
1452    pub collection_id: CollectionUuid,
1453    pub ids: Option<Vec<String>>,
1454    pub r#where: Option<Where>,
1455}
1456
1457impl DeleteCollectionRecordsRequest {
1458    pub fn try_new(
1459        tenant_id: String,
1460        database_name: String,
1461        collection_id: CollectionUuid,
1462        ids: Option<Vec<String>>,
1463        r#where: Option<Where>,
1464    ) -> Result<Self, ChromaValidationError> {
1465        if ids.as_ref().map(|ids| ids.is_empty()).unwrap_or(false) && r#where.is_none() {
1466            return Err(ChromaValidationError::from((
1467                ("ids, where"),
1468                ValidationError::new("filter")
1469                    .with_message("Either ids or where must be specified".into()),
1470            )));
1471        }
1472
1473        let request = Self {
1474            tenant_id,
1475            database_name,
1476            collection_id,
1477            ids,
1478            r#where,
1479        };
1480        request.validate().map_err(ChromaValidationError::from)?;
1481        Ok(request)
1482    }
1483
1484    pub fn into_payload(self) -> Result<DeleteCollectionRecordsPayload, WhereError> {
1485        let where_fields = if let Some(r#where) = self.r#where.as_ref() {
1486            RawWhereFields::from_json_str(Some(&serde_json::to_string(r#where)?), None)?
1487        } else {
1488            RawWhereFields::default()
1489        };
1490        Ok(DeleteCollectionRecordsPayload {
1491            ids: self.ids.clone(),
1492            where_fields,
1493        })
1494    }
1495}
1496
1497#[derive(Serialize, Deserialize)]
1498#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1499pub struct DeleteCollectionRecordsResponse {}
1500
1501#[derive(Error, Debug)]
1502pub enum DeleteCollectionRecordsError {
1503    #[error("Failed to resolve records for deletion: {0}")]
1504    Get(#[from] ExecutorError),
1505    #[error("Backoff and retry")]
1506    Backoff,
1507    #[error(transparent)]
1508    Internal(#[from] Box<dyn ChromaError>),
1509}
1510
1511impl ChromaError for DeleteCollectionRecordsError {
1512    fn code(&self) -> ErrorCodes {
1513        match self {
1514            DeleteCollectionRecordsError::Get(err) => err.code(),
1515            DeleteCollectionRecordsError::Backoff => ErrorCodes::ResourceExhausted,
1516            DeleteCollectionRecordsError::Internal(err) => err.code(),
1517        }
1518    }
1519}
1520
1521////////////////////////// Include //////////////////////////
1522
1523#[derive(Error, Debug)]
1524#[error("Invalid include value: {0}")]
1525pub struct IncludeParsingError(String);
1526
1527impl ChromaError for IncludeParsingError {
1528    fn code(&self) -> ErrorCodes {
1529        ErrorCodes::InvalidArgument
1530    }
1531}
1532
1533#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
1534#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1535pub enum Include {
1536    #[serde(rename = "distances")]
1537    Distance,
1538    #[serde(rename = "documents")]
1539    Document,
1540    #[serde(rename = "embeddings")]
1541    Embedding,
1542    #[serde(rename = "metadatas")]
1543    Metadata,
1544    #[serde(rename = "uris")]
1545    Uri,
1546}
1547
1548impl TryFrom<&str> for Include {
1549    type Error = IncludeParsingError;
1550
1551    fn try_from(value: &str) -> Result<Self, Self::Error> {
1552        match value {
1553            "distances" => Ok(Include::Distance),
1554            "documents" => Ok(Include::Document),
1555            "embeddings" => Ok(Include::Embedding),
1556            "metadatas" => Ok(Include::Metadata),
1557            "uris" => Ok(Include::Uri),
1558            _ => Err(IncludeParsingError(value.to_string())),
1559        }
1560    }
1561}
1562
1563#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
1564#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1565#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
1566pub struct IncludeList(pub Vec<Include>);
1567
1568impl IncludeList {
1569    pub fn empty() -> Self {
1570        Self(Vec::new())
1571    }
1572
1573    pub fn default_query() -> Self {
1574        Self(vec![
1575            Include::Document,
1576            Include::Metadata,
1577            Include::Distance,
1578        ])
1579    }
1580    pub fn default_get() -> Self {
1581        Self(vec![Include::Document, Include::Metadata])
1582    }
1583    pub fn all() -> Self {
1584        Self(vec![
1585            Include::Document,
1586            Include::Metadata,
1587            Include::Distance,
1588            Include::Embedding,
1589            Include::Uri,
1590        ])
1591    }
1592}
1593
1594impl TryFrom<Vec<String>> for IncludeList {
1595    type Error = IncludeParsingError;
1596
1597    fn try_from(value: Vec<String>) -> Result<Self, Self::Error> {
1598        let mut includes = Vec::new();
1599        for v in value {
1600            // "data" is only used by single node Chroma
1601            if v == "data" {
1602                includes.push(Include::Metadata);
1603                continue;
1604            }
1605
1606            includes.push(Include::try_from(v.as_str())?);
1607        }
1608        Ok(IncludeList(includes))
1609    }
1610}
1611
1612////////////////////////// Count //////////////////////////
1613
1614#[non_exhaustive]
1615#[derive(Clone, Deserialize, Serialize, Validate)]
1616pub struct CountRequest {
1617    pub tenant_id: String,
1618    pub database_name: String,
1619    pub collection_id: CollectionUuid,
1620}
1621
1622impl CountRequest {
1623    pub fn try_new(
1624        tenant_id: String,
1625        database_name: String,
1626        collection_id: CollectionUuid,
1627    ) -> Result<Self, ChromaValidationError> {
1628        let request = Self {
1629            tenant_id,
1630            database_name,
1631            collection_id,
1632        };
1633        request.validate().map_err(ChromaValidationError::from)?;
1634        Ok(request)
1635    }
1636}
1637
1638pub type CountResponse = u32;
1639
1640//////////////////////// Payload Err ////////////////////
1641
1642#[derive(Debug, thiserror::Error)]
1643pub enum WhereError {
1644    #[error("serialization: {0}")]
1645    Serialization(#[from] serde_json::Error),
1646    #[error("validation: {0}")]
1647    Validation(#[from] WhereValidationError),
1648}
1649
1650////////////////////////// Get //////////////////////////
1651
1652#[derive(Debug, Clone, Deserialize, Serialize)]
1653#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1654pub struct GetRequestPayload {
1655    pub ids: Option<Vec<String>>,
1656    #[serde(flatten)]
1657    pub where_fields: RawWhereFields,
1658    pub limit: Option<u32>,
1659    pub offset: Option<u32>,
1660    #[serde(default = "IncludeList::default_get")]
1661    pub include: IncludeList,
1662}
1663
1664#[non_exhaustive]
1665#[derive(Debug, Clone, Validate, Serialize)]
1666#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1667pub struct GetRequest {
1668    pub tenant_id: String,
1669    pub database_name: String,
1670    pub collection_id: CollectionUuid,
1671    pub ids: Option<Vec<String>>,
1672    pub r#where: Option<Where>,
1673    pub limit: Option<u32>,
1674    pub offset: u32,
1675    pub include: IncludeList,
1676}
1677
1678impl GetRequest {
1679    #[allow(clippy::too_many_arguments)]
1680    pub fn try_new(
1681        tenant_id: String,
1682        database_name: String,
1683        collection_id: CollectionUuid,
1684        ids: Option<Vec<String>>,
1685        r#where: Option<Where>,
1686        limit: Option<u32>,
1687        offset: u32,
1688        include: IncludeList,
1689    ) -> Result<Self, ChromaValidationError> {
1690        let request = Self {
1691            tenant_id,
1692            database_name,
1693            collection_id,
1694            ids,
1695            r#where,
1696            limit,
1697            offset,
1698            include,
1699        };
1700        request.validate().map_err(ChromaValidationError::from)?;
1701        Ok(request)
1702    }
1703
1704    pub fn into_payload(self) -> Result<GetRequestPayload, WhereError> {
1705        let where_fields = if let Some(r#where) = self.r#where.as_ref() {
1706            RawWhereFields::from_json_str(Some(&serde_json::to_string(r#where)?), None)?
1707        } else {
1708            RawWhereFields::default()
1709        };
1710        Ok(GetRequestPayload {
1711            ids: self.ids,
1712            where_fields,
1713            limit: self.limit,
1714            offset: Some(self.offset),
1715            include: self.include,
1716        })
1717    }
1718}
1719
1720#[derive(Clone, Deserialize, Serialize, Debug, Default)]
1721#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1722#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
1723pub struct GetResponse {
1724    pub ids: Vec<String>,
1725    pub embeddings: Option<Vec<Vec<f32>>>,
1726    pub documents: Option<Vec<Option<String>>>,
1727    pub uris: Option<Vec<Option<String>>>,
1728    // TODO(hammadb): Add metadata & include to the response
1729    pub metadatas: Option<Vec<Option<Metadata>>>,
1730    pub include: Vec<Include>,
1731}
1732
1733impl GetResponse {
1734    pub fn sort_by_ids(&mut self) {
1735        let mut indices: Vec<usize> = (0..self.ids.len()).collect();
1736        indices.sort_by(|&a, &b| self.ids[a].cmp(&self.ids[b]));
1737
1738        let sorted_ids = indices.iter().map(|&i| self.ids[i].clone()).collect();
1739        self.ids = sorted_ids;
1740
1741        if let Some(ref mut embeddings) = self.embeddings {
1742            let sorted_embeddings = indices.iter().map(|&i| embeddings[i].clone()).collect();
1743            *embeddings = sorted_embeddings;
1744        }
1745
1746        if let Some(ref mut documents) = self.documents {
1747            let sorted_docs = indices.iter().map(|&i| documents[i].clone()).collect();
1748            *documents = sorted_docs;
1749        }
1750
1751        if let Some(ref mut uris) = self.uris {
1752            let sorted_uris = indices.iter().map(|&i| uris[i].clone()).collect();
1753            *uris = sorted_uris;
1754        }
1755
1756        if let Some(ref mut metadatas) = self.metadatas {
1757            let sorted_metas = indices.iter().map(|&i| metadatas[i].clone()).collect();
1758            *metadatas = sorted_metas;
1759        }
1760    }
1761}
1762
1763#[cfg(feature = "pyo3")]
1764#[pyo3::pymethods]
1765impl GetResponse {
1766    #[getter]
1767    pub fn ids(&self) -> &Vec<String> {
1768        &self.ids
1769    }
1770
1771    #[getter]
1772    pub fn embeddings(&self) -> Option<Vec<Vec<f32>>> {
1773        self.embeddings.clone()
1774    }
1775
1776    #[getter]
1777    pub fn documents(&self) -> Option<Vec<Option<String>>> {
1778        self.documents.clone()
1779    }
1780
1781    #[getter]
1782    pub fn uris(&self) -> Option<Vec<Option<String>>> {
1783        self.uris.clone()
1784    }
1785
1786    #[getter]
1787    pub fn metadatas(&self) -> Option<Vec<Option<Metadata>>> {
1788        self.metadatas.clone()
1789    }
1790}
1791
1792impl From<(GetResult, IncludeList)> for GetResponse {
1793    fn from((result, IncludeList(include_vec)): (GetResult, IncludeList)) -> Self {
1794        let mut res = Self {
1795            ids: Vec::new(),
1796            embeddings: include_vec
1797                .contains(&Include::Embedding)
1798                .then_some(Vec::new()),
1799            documents: include_vec
1800                .contains(&Include::Document)
1801                .then_some(Vec::new()),
1802            uris: include_vec.contains(&Include::Uri).then_some(Vec::new()),
1803            metadatas: include_vec
1804                .contains(&Include::Metadata)
1805                .then_some(Vec::new()),
1806            include: include_vec,
1807        };
1808        for ProjectionRecord {
1809            id,
1810            document,
1811            embedding,
1812            mut metadata,
1813        } in result.result.records
1814        {
1815            res.ids.push(id);
1816            if let (Some(emb), Some(embeddings)) = (embedding, res.embeddings.as_mut()) {
1817                embeddings.push(emb);
1818            }
1819            if let Some(documents) = res.documents.as_mut() {
1820                documents.push(document);
1821            }
1822            let uri = metadata.as_mut().and_then(|meta| {
1823                meta.remove(CHROMA_URI_KEY).and_then(|v| {
1824                    if let crate::MetadataValue::Str(uri) = v {
1825                        Some(uri)
1826                    } else {
1827                        None
1828                    }
1829                })
1830            });
1831            if let Some(uris) = res.uris.as_mut() {
1832                uris.push(uri);
1833            }
1834
1835            let metadata = metadata.map(|m| {
1836                m.into_iter()
1837                    .filter(|(k, _)| !k.starts_with(CHROMA_KEY))
1838                    .collect()
1839            });
1840            if let Some(metadatas) = res.metadatas.as_mut() {
1841                metadatas.push(metadata);
1842            }
1843        }
1844        res
1845    }
1846}
1847
1848////////////////////////// Query //////////////////////////
1849
1850#[derive(Deserialize, Debug, Clone, Serialize)]
1851#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1852pub struct QueryRequestPayload {
1853    pub ids: Option<Vec<String>>,
1854    #[serde(flatten)]
1855    pub where_fields: RawWhereFields,
1856    pub query_embeddings: Vec<Vec<f32>>,
1857    pub n_results: Option<u32>,
1858    #[serde(default = "IncludeList::default_query")]
1859    pub include: IncludeList,
1860}
1861
1862#[non_exhaustive]
1863#[derive(Debug, Clone, Validate, Serialize)]
1864#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1865pub struct QueryRequest {
1866    pub tenant_id: String,
1867    pub database_name: String,
1868    pub collection_id: CollectionUuid,
1869    pub ids: Option<Vec<String>>,
1870    pub r#where: Option<Where>,
1871    pub embeddings: Vec<Vec<f32>>,
1872    pub n_results: u32,
1873    pub include: IncludeList,
1874}
1875
1876impl QueryRequest {
1877    #[allow(clippy::too_many_arguments)]
1878    pub fn try_new(
1879        tenant_id: String,
1880        database_name: String,
1881        collection_id: CollectionUuid,
1882        ids: Option<Vec<String>>,
1883        r#where: Option<Where>,
1884        embeddings: Vec<Vec<f32>>,
1885        n_results: u32,
1886        include: IncludeList,
1887    ) -> Result<Self, ChromaValidationError> {
1888        let request = Self {
1889            tenant_id,
1890            database_name,
1891            collection_id,
1892            ids,
1893            r#where,
1894            embeddings,
1895            n_results,
1896            include,
1897        };
1898        request.validate().map_err(ChromaValidationError::from)?;
1899        Ok(request)
1900    }
1901
1902    pub fn into_payload(self) -> Result<QueryRequestPayload, WhereError> {
1903        let where_fields = if let Some(r#where) = self.r#where.as_ref() {
1904            RawWhereFields::from_json_str(Some(&serde_json::to_string(r#where)?), None)?
1905        } else {
1906            RawWhereFields::default()
1907        };
1908        Ok(QueryRequestPayload {
1909            ids: self.ids,
1910            where_fields,
1911            query_embeddings: self.embeddings,
1912            n_results: Some(self.n_results),
1913            include: self.include,
1914        })
1915    }
1916}
1917
1918#[derive(Clone, Deserialize, Serialize, Debug)]
1919#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1920#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
1921pub struct QueryResponse {
1922    pub ids: Vec<Vec<String>>,
1923    pub embeddings: Option<Vec<Vec<Option<Vec<f32>>>>>,
1924    pub documents: Option<Vec<Vec<Option<String>>>>,
1925    pub uris: Option<Vec<Vec<Option<String>>>>,
1926    pub metadatas: Option<Vec<Vec<Option<Metadata>>>>,
1927    pub distances: Option<Vec<Vec<Option<f32>>>>,
1928    pub include: Vec<Include>,
1929}
1930
1931impl QueryResponse {
1932    pub fn sort_by_ids(&mut self) {
1933        fn reorder<T: Clone>(v: &mut [T], indices: &[usize]) {
1934            let old = v.to_owned();
1935            for (new_pos, &i) in indices.iter().enumerate() {
1936                v[new_pos] = old[i].clone();
1937            }
1938        }
1939
1940        for i in 0..self.ids.len() {
1941            let mut indices: Vec<usize> = (0..self.ids[i].len()).collect();
1942
1943            indices.sort_unstable_by(|&a, &b| self.ids[i][a].cmp(&self.ids[i][b]));
1944
1945            reorder(&mut self.ids[i], &indices);
1946
1947            if let Some(embeddings) = &mut self.embeddings {
1948                reorder(&mut embeddings[i], &indices);
1949            }
1950
1951            if let Some(documents) = &mut self.documents {
1952                reorder(&mut documents[i], &indices);
1953            }
1954
1955            if let Some(uris) = &mut self.uris {
1956                reorder(&mut uris[i], &indices);
1957            }
1958
1959            if let Some(metadatas) = &mut self.metadatas {
1960                reorder(&mut metadatas[i], &indices);
1961            }
1962
1963            if let Some(distances) = &mut self.distances {
1964                reorder(&mut distances[i], &indices);
1965            }
1966        }
1967    }
1968}
1969
1970#[cfg(feature = "pyo3")]
1971#[pyo3::pymethods]
1972impl QueryResponse {
1973    #[getter]
1974    pub fn ids(&self) -> &Vec<Vec<String>> {
1975        &self.ids
1976    }
1977
1978    #[getter]
1979    pub fn embeddings(&self) -> Option<Vec<Vec<Option<Vec<f32>>>>> {
1980        self.embeddings.clone()
1981    }
1982
1983    #[getter]
1984    pub fn documents(&self) -> Option<Vec<Vec<Option<String>>>> {
1985        self.documents.clone()
1986    }
1987
1988    #[getter]
1989    pub fn uris(&self) -> Option<Vec<Vec<Option<String>>>> {
1990        self.uris.clone()
1991    }
1992
1993    #[getter]
1994    pub fn metadatas(&self) -> Option<Vec<Vec<Option<Metadata>>>> {
1995        self.metadatas.clone()
1996    }
1997
1998    #[getter]
1999    pub fn distances(&self) -> Option<Vec<Vec<Option<f32>>>> {
2000        self.distances.clone()
2001    }
2002}
2003
2004impl From<(KnnBatchResult, IncludeList)> for QueryResponse {
2005    fn from((result, IncludeList(include_vec)): (KnnBatchResult, IncludeList)) -> Self {
2006        let mut res = Self {
2007            ids: Vec::new(),
2008            embeddings: include_vec
2009                .contains(&Include::Embedding)
2010                .then_some(Vec::new()),
2011            documents: include_vec
2012                .contains(&Include::Document)
2013                .then_some(Vec::new()),
2014            uris: include_vec.contains(&Include::Uri).then_some(Vec::new()),
2015            metadatas: include_vec
2016                .contains(&Include::Metadata)
2017                .then_some(Vec::new()),
2018            distances: include_vec
2019                .contains(&Include::Distance)
2020                .then_some(Vec::new()),
2021            include: include_vec,
2022        };
2023        for query_result in result.results {
2024            let mut ids = Vec::new();
2025            let mut embeddings = Vec::new();
2026            let mut documents = Vec::new();
2027            let mut uris = Vec::new();
2028            let mut metadatas = Vec::new();
2029            let mut distances = Vec::new();
2030            for KnnProjectionRecord {
2031                record:
2032                    ProjectionRecord {
2033                        id,
2034                        document,
2035                        embedding,
2036                        mut metadata,
2037                    },
2038                distance,
2039            } in query_result.records
2040            {
2041                ids.push(id);
2042                embeddings.push(embedding);
2043                documents.push(document);
2044
2045                let uri = metadata.as_mut().and_then(|meta| {
2046                    meta.remove(CHROMA_URI_KEY).and_then(|v| {
2047                        if let crate::MetadataValue::Str(uri) = v {
2048                            Some(uri)
2049                        } else {
2050                            None
2051                        }
2052                    })
2053                });
2054                uris.push(uri);
2055
2056                let metadata = metadata.map(|m| {
2057                    m.into_iter()
2058                        .filter(|(k, _)| !k.starts_with(CHROMA_KEY))
2059                        .collect()
2060                });
2061                metadatas.push(metadata);
2062
2063                distances.push(distance);
2064            }
2065            res.ids.push(ids);
2066
2067            if let Some(res_embs) = res.embeddings.as_mut() {
2068                res_embs.push(embeddings);
2069            }
2070            if let Some(res_docs) = res.documents.as_mut() {
2071                res_docs.push(documents);
2072            }
2073            if let Some(res_uri) = res.uris.as_mut() {
2074                res_uri.push(uris);
2075            }
2076            if let Some(res_metas) = res.metadatas.as_mut() {
2077                res_metas.push(metadatas);
2078            }
2079            if let Some(res_dists) = res.distances.as_mut() {
2080                res_dists.push(distances);
2081            }
2082        }
2083        res
2084    }
2085}
2086
2087#[derive(Debug, Clone, Deserialize, Serialize)]
2088#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
2089pub struct SearchRequestPayload {
2090    pub searches: Vec<SearchPayload>,
2091}
2092
2093#[non_exhaustive]
2094#[derive(Clone, Debug, Serialize, Validate)]
2095#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
2096pub struct SearchRequest {
2097    pub tenant_id: String,
2098    pub database_name: String,
2099    pub collection_id: CollectionUuid,
2100    pub searches: Vec<SearchPayload>,
2101}
2102
2103impl SearchRequest {
2104    pub fn try_new(
2105        tenant_id: String,
2106        database_name: String,
2107        collection_id: CollectionUuid,
2108        searches: Vec<SearchPayload>,
2109    ) -> Result<Self, ChromaValidationError> {
2110        let request = Self {
2111            tenant_id,
2112            database_name,
2113            collection_id,
2114            searches,
2115        };
2116        request.validate().map_err(ChromaValidationError::from)?;
2117        Ok(request)
2118    }
2119
2120    pub fn into_payload(self) -> SearchRequestPayload {
2121        SearchRequestPayload {
2122            searches: self.searches,
2123        }
2124    }
2125}
2126
2127#[derive(Clone, Deserialize, Serialize, Debug)]
2128#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
2129pub struct SearchResponse {
2130    pub ids: Vec<Vec<String>>,
2131    pub documents: Vec<Option<Vec<Option<String>>>>,
2132    pub embeddings: Vec<Option<Vec<Option<Vec<f32>>>>>,
2133    pub metadatas: Vec<Option<Vec<Option<Metadata>>>>,
2134    pub scores: Vec<Option<Vec<Option<f32>>>>,
2135    pub select: Vec<Vec<Key>>,
2136}
2137
2138impl From<(SearchResult, Vec<SearchPayload>)> for SearchResponse {
2139    fn from((result, payloads): (SearchResult, Vec<SearchPayload>)) -> Self {
2140        let num_payloads = payloads.len();
2141        let mut res = Self {
2142            ids: Vec::with_capacity(num_payloads),
2143            documents: Vec::with_capacity(num_payloads),
2144            embeddings: Vec::with_capacity(num_payloads),
2145            metadatas: Vec::with_capacity(num_payloads),
2146            scores: Vec::with_capacity(num_payloads),
2147            select: Vec::with_capacity(num_payloads),
2148        };
2149
2150        for (payload_result, payload) in result.results.into_iter().zip(payloads) {
2151            // Get the sorted keys for this payload
2152            let mut payload_select = Vec::from_iter(payload.select.keys.iter().cloned());
2153            payload_select.sort();
2154
2155            let num_records = payload_result.records.len();
2156            let mut ids = Vec::with_capacity(num_records);
2157            let mut documents = Vec::with_capacity(num_records);
2158            let mut embeddings = Vec::with_capacity(num_records);
2159            let mut metadatas = Vec::with_capacity(num_records);
2160            let mut scores = Vec::with_capacity(num_records);
2161
2162            for record in payload_result.records {
2163                ids.push(record.id);
2164                documents.push(record.document);
2165                embeddings.push(record.embedding);
2166                metadatas.push(record.metadata);
2167                scores.push(record.score);
2168            }
2169
2170            res.ids.push(ids);
2171            res.select.push(payload_select.clone());
2172
2173            // Push documents if requested by this payload, otherwise None
2174            res.documents.push(
2175                payload_select
2176                    .binary_search(&Key::Document)
2177                    .is_ok()
2178                    .then_some(documents),
2179            );
2180
2181            // Push embeddings if requested by this payload, otherwise None
2182            res.embeddings.push(
2183                payload_select
2184                    .binary_search(&Key::Embedding)
2185                    .is_ok()
2186                    .then_some(embeddings),
2187            );
2188
2189            // Push metadatas if requested by this payload, otherwise None
2190            // Include if either Key::Metadata is present or any Key::MetadataField(_)
2191            let has_metadata = payload_select.binary_search(&Key::Metadata).is_ok()
2192                || payload_select
2193                    .last()
2194                    .is_some_and(|field| matches!(field, Key::MetadataField(_)));
2195            res.metadatas.push(has_metadata.then_some(metadatas));
2196
2197            // Push scores if requested by this payload, otherwise None
2198            res.scores.push(
2199                payload_select
2200                    .binary_search(&Key::Score)
2201                    .is_ok()
2202                    .then_some(scores),
2203            );
2204        }
2205
2206        res
2207    }
2208}
2209
2210#[derive(Error, Debug)]
2211pub enum QueryError {
2212    #[error("Error executing plan: {0}")]
2213    Executor(#[from] ExecutorError),
2214    #[error(transparent)]
2215    Other(#[from] Box<dyn ChromaError>),
2216}
2217
2218impl ChromaError for QueryError {
2219    fn code(&self) -> ErrorCodes {
2220        match self {
2221            QueryError::Executor(e) => e.code(),
2222            QueryError::Other(err) => err.code(),
2223        }
2224    }
2225}
2226
2227#[derive(Serialize)]
2228#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
2229pub struct HealthCheckResponse {
2230    pub is_executor_ready: bool,
2231    pub is_log_client_ready: bool,
2232}
2233
2234impl HealthCheckResponse {
2235    pub fn get_status_code(&self) -> tonic::Code {
2236        if self.is_executor_ready && self.is_log_client_ready {
2237            tonic::Code::Ok
2238        } else {
2239            tonic::Code::Unavailable
2240        }
2241    }
2242}
2243
2244#[derive(Debug, Error)]
2245pub enum ExecutorError {
2246    #[error("Error converting: {0}")]
2247    Conversion(#[from] QueryConversionError),
2248    #[error("Error converting plan to proto: {0}")]
2249    PlanToProto(#[from] PlanToProtoError),
2250    #[error(transparent)]
2251    Grpc(#[from] Status),
2252    #[error("Inconsistent data")]
2253    InconsistentData,
2254    #[error("Collection is missing HNSW configuration")]
2255    CollectionMissingHnswConfiguration,
2256    #[error("Internal error: {0}")]
2257    Internal(Box<dyn ChromaError>),
2258    #[error("Error sending backfill request to compactor: {0}")]
2259    BackfillError(Box<dyn ChromaError>),
2260    #[error("Not implemented: {0}")]
2261    NotImplemented(String),
2262}
2263
2264impl ChromaError for ExecutorError {
2265    fn code(&self) -> ErrorCodes {
2266        match self {
2267            ExecutorError::Conversion(_) => ErrorCodes::InvalidArgument,
2268            ExecutorError::PlanToProto(_) => ErrorCodes::Internal,
2269            ExecutorError::Grpc(e) => e.code().into(),
2270            ExecutorError::InconsistentData => ErrorCodes::Internal,
2271            ExecutorError::CollectionMissingHnswConfiguration => ErrorCodes::Internal,
2272            ExecutorError::Internal(e) => e.code(),
2273            ExecutorError::BackfillError(e) => e.code(),
2274            ExecutorError::NotImplemented(_) => ErrorCodes::Unimplemented,
2275        }
2276    }
2277}
2278
2279//////////////////////////  Attached Function Operations //////////////////////////
2280
2281#[non_exhaustive]
2282#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
2283#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
2284pub struct AttachFunctionRequest {
2285    #[validate(length(min = 1))]
2286    pub name: String,
2287    pub function_id: String,
2288    pub output_collection: String,
2289    #[serde(default = "default_empty_json_object")]
2290    pub params: serde_json::Value,
2291}
2292
2293fn default_empty_json_object() -> serde_json::Value {
2294    serde_json::json!({})
2295}
2296
2297impl AttachFunctionRequest {
2298    pub fn try_new(
2299        name: String,
2300        function_id: String,
2301        output_collection: String,
2302        params: serde_json::Value,
2303    ) -> Result<Self, ChromaValidationError> {
2304        let request = Self {
2305            name,
2306            function_id,
2307            output_collection,
2308            params,
2309        };
2310        request.validate().map_err(ChromaValidationError::from)?;
2311        Ok(request)
2312    }
2313}
2314
2315#[derive(Clone, Debug, Serialize)]
2316#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
2317pub struct AttachedFunctionInfo {
2318    pub id: String,
2319    pub name: String,
2320    pub function_id: String,
2321}
2322
2323#[derive(Clone, Debug, Serialize)]
2324#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
2325pub struct AttachFunctionResponse {
2326    pub attached_function: AttachedFunctionInfo,
2327}
2328
2329#[derive(Error, Debug)]
2330pub enum AttachFunctionError {
2331    #[error(" Attached Function with name [{0}] already exists")]
2332    AlreadyExists(String),
2333    #[error("Input collection [{0}] does not exist")]
2334    InputCollectionNotFound(String),
2335    #[error("Output collection [{0}] already exists")]
2336    OutputCollectionExists(String),
2337    #[error(transparent)]
2338    Validation(#[from] ChromaValidationError),
2339    #[error(transparent)]
2340    Internal(#[from] Box<dyn ChromaError>),
2341}
2342
2343impl ChromaError for AttachFunctionError {
2344    fn code(&self) -> ErrorCodes {
2345        match self {
2346            AttachFunctionError::AlreadyExists(_) => ErrorCodes::AlreadyExists,
2347            AttachFunctionError::InputCollectionNotFound(_) => ErrorCodes::NotFound,
2348            AttachFunctionError::OutputCollectionExists(_) => ErrorCodes::AlreadyExists,
2349            AttachFunctionError::Validation(err) => err.code(),
2350            AttachFunctionError::Internal(err) => err.code(),
2351        }
2352    }
2353}
2354
2355#[non_exhaustive]
2356#[derive(Clone, Debug, Deserialize, Validate, Serialize)]
2357#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
2358pub struct DetachFunctionRequest {
2359    /// Whether to delete the output collection as well
2360    #[serde(default)]
2361    pub delete_output: bool,
2362}
2363
2364impl DetachFunctionRequest {
2365    pub fn try_new(delete_output: bool) -> Result<Self, ChromaValidationError> {
2366        let request = Self { delete_output };
2367        request.validate().map_err(ChromaValidationError::from)?;
2368        Ok(request)
2369    }
2370}
2371
2372#[derive(Clone, Debug, Serialize)]
2373#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
2374pub struct DetachFunctionResponse {
2375    pub success: bool,
2376}
2377
2378#[derive(Error, Debug)]
2379pub enum DetachFunctionError {
2380    #[error(" Attached Function with ID [{0}] does not exist")]
2381    NotFound(String),
2382    #[error(transparent)]
2383    Validation(#[from] ChromaValidationError),
2384    #[error(transparent)]
2385    Internal(#[from] Box<dyn ChromaError>),
2386}
2387
2388impl ChromaError for DetachFunctionError {
2389    fn code(&self) -> ErrorCodes {
2390        match self {
2391            DetachFunctionError::NotFound(_) => ErrorCodes::NotFound,
2392            DetachFunctionError::Validation(err) => err.code(),
2393            DetachFunctionError::Internal(err) => err.code(),
2394        }
2395    }
2396}
2397
2398#[cfg(test)]
2399mod test {
2400    use super::*;
2401    use crate::{MetadataValue, SparseVector, UpdateMetadataValue};
2402    use std::collections::HashMap;
2403
2404    #[test]
2405    fn test_create_database_min_length() {
2406        let request = CreateDatabaseRequest::try_new("default_tenant".to_string(), "a".to_string());
2407        assert!(request.is_err());
2408    }
2409
2410    #[test]
2411    fn test_create_tenant_min_length() {
2412        let request = CreateTenantRequest::try_new("a".to_string());
2413        assert!(request.is_err());
2414    }
2415
2416    #[test]
2417    fn test_add_request_validates_sparse_vectors() {
2418        let mut metadata = HashMap::new();
2419        // Add unsorted sparse vector - should fail validation
2420        metadata.insert(
2421            "sparse".to_string(),
2422            MetadataValue::SparseVector(SparseVector::new(vec![3, 1, 2], vec![0.3, 0.1, 0.2])),
2423        );
2424
2425        let result = AddCollectionRecordsRequest::try_new(
2426            "tenant".to_string(),
2427            "database".to_string(),
2428            CollectionUuid(uuid::Uuid::new_v4()),
2429            vec!["id1".to_string()],
2430            vec![vec![0.1, 0.2]],
2431            None,
2432            None,
2433            Some(vec![Some(metadata)]),
2434        );
2435
2436        // Should fail because sparse vector is not sorted
2437        assert!(result.is_err());
2438    }
2439
2440    #[test]
2441    fn test_update_request_validates_sparse_vectors() {
2442        let mut metadata = HashMap::new();
2443        // Add unsorted sparse vector - should fail validation
2444        metadata.insert(
2445            "sparse".to_string(),
2446            UpdateMetadataValue::SparseVector(SparseVector::new(
2447                vec![3, 1, 2],
2448                vec![0.3, 0.1, 0.2],
2449            )),
2450        );
2451
2452        let result = UpdateCollectionRecordsRequest::try_new(
2453            "tenant".to_string(),
2454            "database".to_string(),
2455            CollectionUuid(uuid::Uuid::new_v4()),
2456            vec!["id1".to_string()],
2457            None,
2458            None,
2459            None,
2460            Some(vec![Some(metadata)]),
2461        );
2462
2463        // Should fail because sparse vector is not sorted
2464        assert!(result.is_err());
2465    }
2466
2467    #[test]
2468    fn test_upsert_request_validates_sparse_vectors() {
2469        let mut metadata = HashMap::new();
2470        // Add unsorted sparse vector - should fail validation
2471        metadata.insert(
2472            "sparse".to_string(),
2473            UpdateMetadataValue::SparseVector(SparseVector::new(
2474                vec![3, 1, 2],
2475                vec![0.3, 0.1, 0.2],
2476            )),
2477        );
2478
2479        let result = UpsertCollectionRecordsRequest::try_new(
2480            "tenant".to_string(),
2481            "database".to_string(),
2482            CollectionUuid(uuid::Uuid::new_v4()),
2483            vec!["id1".to_string()],
2484            vec![vec![0.1, 0.2]],
2485            None,
2486            None,
2487            Some(vec![Some(metadata)]),
2488        );
2489
2490        // Should fail because sparse vector is not sorted
2491        assert!(result.is_err());
2492    }
2493}