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