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