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