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