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