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