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::DistributedSpannParametersFromSegmentError;
28use crate::EmbeddingsPayload;
29use crate::HnswParametersFromSegmentError;
30use crate::Metadata;
31use crate::RawWhereFields;
32use crate::Schema;
33use crate::SchemaError;
34use crate::SegmentConversionError;
35use crate::SegmentScopeConversionError;
36use crate::UpdateEmbeddingsPayload;
37use crate::UpdateMetadata;
38use crate::Where;
39use crate::WhereValidationError;
40use chroma_error::ChromaValidationError;
41use chroma_error::{ChromaError, ErrorCodes};
42use serde::Deserialize;
43use serde::Serialize;
44use std::time::SystemTimeError;
45use thiserror::Error;
46use tonic::Status;
47use uuid::Uuid;
48use validator::Validate;
49use validator::ValidationError;
50
51#[cfg(feature = "pyo3")]
52use pyo3::types::PyAnyMethods;
53
54#[derive(Debug, Error)]
55pub enum GetSegmentsError {
56 #[error("Could not parse segment")]
57 SegmentConversion(#[from] SegmentConversionError),
58 #[error("Unknown segment scope")]
59 UnknownScope(#[from] SegmentScopeConversionError),
60 #[error(transparent)]
61 Internal(#[from] Box<dyn ChromaError>),
62}
63
64impl ChromaError for GetSegmentsError {
65 fn code(&self) -> ErrorCodes {
66 match self {
67 GetSegmentsError::SegmentConversion(_) => ErrorCodes::Internal,
68 GetSegmentsError::UnknownScope(_) => ErrorCodes::Internal,
69 GetSegmentsError::Internal(err) => err.code(),
70 }
71 }
72}
73
74#[derive(Debug, Error)]
75pub enum GetCollectionWithSegmentsError {
76 #[error("Failed to convert proto collection")]
77 CollectionConversionError(#[from] CollectionConversionError),
78 #[error("Duplicate segment")]
79 DuplicateSegment,
80 #[error("Missing field: [{0}]")]
81 Field(String),
82 #[error("Failed to convert proto segment")]
83 SegmentConversionError(#[from] SegmentConversionError),
84 #[error("Failed to get segments")]
85 GetSegmentsError(#[from] GetSegmentsError),
86 #[error("Grpc error: {0}")]
87 Grpc(#[from] Status),
88 #[error("Collection [{0}] does not exist.")]
89 NotFound(String),
90 #[error(transparent)]
91 Internal(#[from] Box<dyn ChromaError>),
92}
93
94impl ChromaError for GetCollectionWithSegmentsError {
95 fn code(&self) -> ErrorCodes {
96 match self {
97 GetCollectionWithSegmentsError::CollectionConversionError(
98 collection_conversion_error,
99 ) => collection_conversion_error.code(),
100 GetCollectionWithSegmentsError::DuplicateSegment => ErrorCodes::Internal,
101 GetCollectionWithSegmentsError::Field(_) => ErrorCodes::FailedPrecondition,
102 GetCollectionWithSegmentsError::SegmentConversionError(segment_conversion_error) => {
103 segment_conversion_error.code()
104 }
105 GetCollectionWithSegmentsError::Grpc(status) => status.code().into(),
106 GetCollectionWithSegmentsError::GetSegmentsError(get_segments_error) => {
107 get_segments_error.code()
108 }
109 GetCollectionWithSegmentsError::NotFound(_) => ErrorCodes::NotFound,
110 GetCollectionWithSegmentsError::Internal(err) => err.code(),
111 }
112 }
113
114 fn should_trace_error(&self) -> bool {
115 if let Self::Grpc(status) = self {
116 status.code() != ErrorCodes::NotFound.into()
117 } else {
118 true
119 }
120 }
121}
122
123#[derive(Debug, Error)]
124pub enum BatchGetCollectionVersionFilePathsError {
125 #[error("Grpc error: {0}")]
126 Grpc(#[from] Status),
127 #[error("Could not parse UUID from string {1}: {0}")]
128 Uuid(uuid::Error, String),
129}
130
131impl ChromaError for BatchGetCollectionVersionFilePathsError {
132 fn code(&self) -> ErrorCodes {
133 match self {
134 BatchGetCollectionVersionFilePathsError::Grpc(status) => status.code().into(),
135 BatchGetCollectionVersionFilePathsError::Uuid(_, _) => ErrorCodes::InvalidArgument,
136 }
137 }
138}
139
140#[derive(Debug, Error)]
141pub enum BatchGetCollectionSoftDeleteStatusError {
142 #[error("Grpc error: {0}")]
143 Grpc(#[from] Status),
144 #[error("Could not parse UUID from string {1}: {0}")]
145 Uuid(uuid::Error, String),
146}
147
148impl ChromaError for BatchGetCollectionSoftDeleteStatusError {
149 fn code(&self) -> ErrorCodes {
150 match self {
151 BatchGetCollectionSoftDeleteStatusError::Grpc(status) => status.code().into(),
152 BatchGetCollectionSoftDeleteStatusError::Uuid(_, _) => ErrorCodes::InvalidArgument,
153 }
154 }
155}
156
157#[derive(Serialize)]
158#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
159pub struct ResetResponse {}
160
161#[derive(Debug, Error)]
162pub enum ResetError {
163 #[error(transparent)]
164 Cache(Box<dyn ChromaError>),
165 #[error(transparent)]
166 Internal(#[from] Box<dyn ChromaError>),
167 #[error("Reset is disabled by config")]
168 NotAllowed,
169}
170
171impl ChromaError for ResetError {
172 fn code(&self) -> ErrorCodes {
173 match self {
174 ResetError::Cache(err) => err.code(),
175 ResetError::Internal(err) => err.code(),
176 ResetError::NotAllowed => ErrorCodes::PermissionDenied,
177 }
178 }
179}
180
181#[derive(Serialize)]
182#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
183pub struct ChecklistResponse {
184 pub max_batch_size: u32,
185 pub supports_base64_encoding: bool,
186}
187
188#[derive(Debug, Error)]
189#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
190pub enum HeartbeatError {
191 #[error("system time error: {0}")]
192 CouldNotGetTime(String),
193}
194
195impl From<SystemTimeError> for HeartbeatError {
196 fn from(err: SystemTimeError) -> Self {
197 HeartbeatError::CouldNotGetTime(err.to_string())
198 }
199}
200
201impl ChromaError for HeartbeatError {
202 fn code(&self) -> ErrorCodes {
203 ErrorCodes::Internal
204 }
205}
206
207#[non_exhaustive]
208#[derive(Serialize, Validate, Deserialize)]
209#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
210pub struct CreateTenantRequest {
211 #[validate(length(min = 3))]
212 pub name: String,
213}
214
215impl CreateTenantRequest {
216 pub fn try_new(name: String) -> Result<Self, ChromaValidationError> {
217 let request = Self { name };
218 request.validate().map_err(ChromaValidationError::from)?;
219 Ok(request)
220 }
221}
222
223#[derive(Serialize, Deserialize)]
224#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
225pub struct CreateTenantResponse {}
226
227#[derive(Debug, Error)]
228pub enum CreateTenantError {
229 #[error("Tenant [{0}] already exists")]
230 AlreadyExists(String),
231 #[error(transparent)]
232 Internal(#[from] Box<dyn ChromaError>),
233}
234
235impl ChromaError for CreateTenantError {
236 fn code(&self) -> ErrorCodes {
237 match self {
238 CreateTenantError::AlreadyExists(_) => ErrorCodes::AlreadyExists,
239 CreateTenantError::Internal(err) => err.code(),
240 }
241 }
242}
243
244#[non_exhaustive]
245#[derive(Validate, Serialize)]
246#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
247pub struct GetTenantRequest {
248 pub name: String,
249}
250
251impl GetTenantRequest {
252 pub fn try_new(name: String) -> Result<Self, ChromaValidationError> {
253 let request = Self { name };
254 request.validate().map_err(ChromaValidationError::from)?;
255 Ok(request)
256 }
257}
258
259#[derive(Serialize)]
260#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
261#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
262pub struct GetTenantResponse {
263 pub name: String,
264 pub resource_name: Option<String>,
265}
266
267#[cfg(feature = "pyo3")]
268#[pyo3::pymethods]
269impl GetTenantResponse {
270 #[getter]
271 pub fn name(&self) -> &String {
272 &self.name
273 }
274
275 #[getter]
276 pub fn resource_name(&self) -> Option<String> {
277 self.resource_name.clone()
278 }
279}
280
281#[derive(Debug, Error)]
282pub enum GetTenantError {
283 #[error(transparent)]
284 Internal(#[from] Box<dyn ChromaError>),
285 #[error("Tenant [{0}] not found")]
286 NotFound(String),
287}
288
289impl ChromaError for GetTenantError {
290 fn code(&self) -> ErrorCodes {
291 match self {
292 GetTenantError::Internal(err) => err.code(),
293 GetTenantError::NotFound(_) => ErrorCodes::NotFound,
294 }
295 }
296}
297
298#[non_exhaustive]
299#[derive(Validate, Serialize)]
300#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
301pub struct UpdateTenantRequest {
302 pub tenant_id: String,
303 pub resource_name: String,
304}
305
306impl UpdateTenantRequest {
307 pub fn try_new(
308 tenant_id: String,
309 resource_name: String,
310 ) -> Result<Self, ChromaValidationError> {
311 let request = Self {
312 tenant_id,
313 resource_name,
314 };
315 request.validate().map_err(ChromaValidationError::from)?;
316 Ok(request)
317 }
318}
319
320#[derive(Serialize)]
321#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
322#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
323pub struct UpdateTenantResponse {}
324
325#[cfg(feature = "pyo3")]
326#[pyo3::pymethods]
327impl UpdateTenantResponse {}
328
329#[derive(Error, Debug)]
330pub enum UpdateTenantError {
331 #[error("Failed to set resource name")]
332 FailedToSetResourceName(#[from] tonic::Status),
333 #[error(transparent)]
334 Internal(#[from] Box<dyn ChromaError>),
335 #[error("Tenant [{0}] not found")]
336 NotFound(String),
337}
338
339impl ChromaError for UpdateTenantError {
340 fn code(&self) -> ErrorCodes {
341 match self {
342 UpdateTenantError::FailedToSetResourceName(_) => ErrorCodes::AlreadyExists,
343 UpdateTenantError::Internal(err) => err.code(),
344 UpdateTenantError::NotFound(_) => ErrorCodes::NotFound,
345 }
346 }
347}
348
349#[non_exhaustive]
350#[derive(Validate, Serialize)]
351#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
352pub struct CreateDatabaseRequest {
353 pub database_id: Uuid,
354 pub tenant_id: String,
355 #[validate(length(min = 3))]
356 pub database_name: String,
357}
358
359impl CreateDatabaseRequest {
360 pub fn try_new(
361 tenant_id: String,
362 database_name: String,
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: String,
507}
508
509impl GetDatabaseRequest {
510 pub fn try_new(
511 tenant_id: String,
512 database_name: String,
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: String,
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: String,
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: String,
644}
645
646impl CountCollectionsRequest {
647 pub fn try_new(
648 tenant_id: String,
649 database_name: String,
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: String,
668 pub collection_name: String,
669}
670
671impl GetCollectionRequest {
672 pub fn try_new(
673 tenant_id: String,
674 database_name: String,
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: String,
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: String,
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 collection_id: CollectionUuid,
923 #[validate(custom(function = "validate_name"))]
924 pub new_name: Option<String>,
925 #[validate(custom(function = "validate_non_empty_collection_update_metadata"))]
926 pub new_metadata: Option<CollectionMetadataUpdate>,
927 pub new_configuration: Option<InternalUpdateCollectionConfiguration>,
928}
929
930impl UpdateCollectionRequest {
931 pub fn try_new(
932 collection_id: CollectionUuid,
933 new_name: Option<String>,
934 new_metadata: Option<CollectionMetadataUpdate>,
935 new_configuration: Option<InternalUpdateCollectionConfiguration>,
936 ) -> Result<Self, ChromaValidationError> {
937 let request = Self {
938 collection_id,
939 new_name,
940 new_metadata,
941 new_configuration,
942 };
943 request.validate().map_err(ChromaValidationError::from)?;
944 Ok(request)
945 }
946}
947
948#[derive(Serialize)]
949#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
950pub struct UpdateCollectionResponse {}
951
952#[derive(Error, Debug)]
953pub enum UpdateCollectionError {
954 #[error("Collection [{0}] does not exist")]
955 NotFound(String),
956 #[error("Metadata reset unsupported")]
957 MetadataResetUnsupported,
958 #[error("Could not serialize configuration")]
959 Configuration(#[source] serde_json::Error),
960 #[error(transparent)]
961 Internal(#[from] Box<dyn ChromaError>),
962 #[error("Could not parse config: {0}")]
963 InvalidConfig(#[from] CollectionConfigurationToInternalConfigurationError),
964 #[error("SPANN is still in development. Not allowed to created spann indexes")]
965 SpannNotImplemented,
966 #[error("Could not serialize schema: {0}")]
967 Schema(#[source] serde_json::Error),
968}
969
970impl ChromaError for UpdateCollectionError {
971 fn code(&self) -> ErrorCodes {
972 match self {
973 UpdateCollectionError::NotFound(_) => ErrorCodes::NotFound,
974 UpdateCollectionError::MetadataResetUnsupported => ErrorCodes::InvalidArgument,
975 UpdateCollectionError::Configuration(_) => ErrorCodes::Internal,
976 UpdateCollectionError::Internal(err) => err.code(),
977 UpdateCollectionError::InvalidConfig(_) => ErrorCodes::InvalidArgument,
978 UpdateCollectionError::SpannNotImplemented => ErrorCodes::InvalidArgument,
979 UpdateCollectionError::Schema(_) => ErrorCodes::Internal,
980 }
981 }
982}
983
984#[non_exhaustive]
985#[derive(Clone, Validate, Serialize)]
986#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
987pub struct DeleteCollectionRequest {
988 pub tenant_id: String,
989 pub database_name: String,
990 pub collection_name: String,
991}
992
993impl DeleteCollectionRequest {
994 pub fn try_new(
995 tenant_id: String,
996 database_name: String,
997 collection_name: String,
998 ) -> Result<Self, ChromaValidationError> {
999 let request = Self {
1000 tenant_id,
1001 database_name,
1002 collection_name,
1003 };
1004 request.validate().map_err(ChromaValidationError::from)?;
1005 Ok(request)
1006 }
1007}
1008
1009#[derive(Serialize)]
1010#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1011pub struct DeleteCollectionResponse {}
1012
1013#[derive(Error, Debug)]
1014pub enum DeleteCollectionError {
1015 #[error("Collection [{0}] does not exist")]
1016 NotFound(String),
1017 #[error(transparent)]
1018 Validation(#[from] ChromaValidationError),
1019 #[error(transparent)]
1020 Get(#[from] GetCollectionError),
1021 #[error(transparent)]
1022 Internal(#[from] Box<dyn ChromaError>),
1023}
1024
1025impl ChromaError for DeleteCollectionError {
1026 fn code(&self) -> ErrorCodes {
1027 match self {
1028 DeleteCollectionError::Validation(err) => err.code(),
1029 DeleteCollectionError::NotFound(_) => ErrorCodes::NotFound,
1030 DeleteCollectionError::Get(err) => err.code(),
1031 DeleteCollectionError::Internal(err) => err.code(),
1032 }
1033 }
1034}
1035
1036#[derive(Serialize, Deserialize)]
1037#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1038pub struct IndexStatusResponse {
1039 pub op_indexing_progress: f32,
1040 pub num_unindexed_ops: u64,
1041 pub num_indexed_ops: u64,
1042 pub total_ops: u64,
1043}
1044
1045#[derive(Error, Debug)]
1046pub enum IndexStatusError {
1047 #[error("Collection [{0}] does not exist")]
1048 NotFound(String),
1049 #[error(transparent)]
1050 Internal(#[from] Box<dyn ChromaError>),
1051}
1052
1053impl From<GetCollectionError> for IndexStatusError {
1054 fn from(err: GetCollectionError) -> Self {
1055 match err {
1056 GetCollectionError::NotFound(msg) => IndexStatusError::NotFound(msg),
1057 other => IndexStatusError::Internal(Box::new(other)),
1058 }
1059 }
1060}
1061
1062impl ChromaError for IndexStatusError {
1063 fn code(&self) -> ErrorCodes {
1064 match self {
1065 IndexStatusError::NotFound(_) => ErrorCodes::NotFound,
1066 IndexStatusError::Internal(err) => err.code(),
1067 }
1068 }
1069}
1070
1071#[non_exhaustive]
1072#[derive(Clone, Validate, Serialize)]
1073#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1074pub struct ForkCollectionRequest {
1075 pub tenant_id: String,
1076 pub database_name: String,
1077 pub source_collection_id: CollectionUuid,
1078 pub target_collection_name: String,
1079}
1080
1081impl ForkCollectionRequest {
1082 pub fn try_new(
1083 tenant_id: String,
1084 database_name: String,
1085 source_collection_id: CollectionUuid,
1086 target_collection_name: String,
1087 ) -> Result<Self, ChromaValidationError> {
1088 let request = Self {
1089 tenant_id,
1090 database_name,
1091 source_collection_id,
1092 target_collection_name,
1093 };
1094 request.validate().map_err(ChromaValidationError::from)?;
1095 Ok(request)
1096 }
1097}
1098
1099pub type ForkCollectionResponse = Collection;
1100
1101#[derive(Clone, Debug)]
1102pub struct ForkLogsResponse {
1103 pub compaction_offset: u64,
1104 pub enumeration_offset: u64,
1105}
1106
1107#[derive(Error, Debug)]
1108pub enum ForkCollectionError {
1109 #[error("Collection [{0}] already exists")]
1110 AlreadyExists(String),
1111 #[error("Failed to convert proto collection")]
1112 CollectionConversionError(#[from] CollectionConversionError),
1113 #[error("Duplicate segment")]
1114 DuplicateSegment,
1115 #[error("Missing field: [{0}]")]
1116 Field(String),
1117 #[error("Collection forking is unsupported for local chroma")]
1118 Local,
1119 #[error(transparent)]
1120 Internal(#[from] Box<dyn ChromaError>),
1121 #[error("Collection [{0}] does not exist")]
1122 NotFound(String),
1123 #[error("Failed to convert proto segment")]
1124 SegmentConversionError(#[from] SegmentConversionError),
1125 #[error("Failed to reconcile schema: {0}")]
1126 InvalidSchema(#[from] SchemaError),
1127}
1128
1129impl ChromaError for ForkCollectionError {
1130 fn code(&self) -> ErrorCodes {
1131 match self {
1132 ForkCollectionError::NotFound(_) => ErrorCodes::NotFound,
1133 ForkCollectionError::AlreadyExists(_) => ErrorCodes::AlreadyExists,
1134 ForkCollectionError::CollectionConversionError(e) => e.code(),
1135 ForkCollectionError::DuplicateSegment => ErrorCodes::Internal,
1136 ForkCollectionError::Field(_) => ErrorCodes::FailedPrecondition,
1137 ForkCollectionError::Local => ErrorCodes::Unimplemented,
1138 ForkCollectionError::Internal(e) => e.code(),
1139 ForkCollectionError::SegmentConversionError(e) => e.code(),
1140 ForkCollectionError::InvalidSchema(e) => e.code(),
1141 }
1142 }
1143}
1144
1145#[derive(Debug, Error)]
1146pub enum CountForksError {
1147 #[error("Collection [{0}] does not exist")]
1148 NotFound(String),
1149 #[error(transparent)]
1150 Internal(#[from] Box<dyn ChromaError>),
1151 #[error("Count forks is unsupported for local chroma")]
1152 Local,
1153}
1154
1155impl ChromaError for CountForksError {
1156 fn code(&self) -> ErrorCodes {
1157 match self {
1158 CountForksError::NotFound(_) => ErrorCodes::NotFound,
1159 CountForksError::Internal(chroma_error) => chroma_error.code(),
1160 CountForksError::Local => ErrorCodes::Unimplemented,
1161 }
1162 }
1163}
1164
1165#[derive(Debug, Error)]
1166pub enum ListAttachedFunctionsError {
1167 #[error("Collection [{0}] does not exist")]
1168 NotFound(String),
1169 #[error(transparent)]
1170 Internal(#[from] Box<dyn ChromaError>),
1171 #[error("List attached functions is not implemented")]
1172 NotImplemented,
1173}
1174
1175impl ChromaError for ListAttachedFunctionsError {
1176 fn code(&self) -> ErrorCodes {
1177 match self {
1178 ListAttachedFunctionsError::NotFound(_) => ErrorCodes::NotFound,
1179 ListAttachedFunctionsError::Internal(chroma_error) => chroma_error.code(),
1180 ListAttachedFunctionsError::NotImplemented => ErrorCodes::Unimplemented,
1181 }
1182 }
1183}
1184
1185#[derive(Debug, Error)]
1186pub enum GetCollectionSizeError {
1187 #[error(transparent)]
1188 Internal(#[from] Box<dyn ChromaError>),
1189 #[error("Collection [{0}] does not exist")]
1190 NotFound(String),
1191}
1192
1193impl ChromaError for GetCollectionSizeError {
1194 fn code(&self) -> ErrorCodes {
1195 match self {
1196 GetCollectionSizeError::Internal(err) => err.code(),
1197 GetCollectionSizeError::NotFound(_) => ErrorCodes::NotFound,
1198 }
1199 }
1200}
1201
1202#[derive(Error, Debug)]
1203pub enum ListCollectionVersionsError {
1204 #[error(transparent)]
1205 Internal(#[from] Box<dyn ChromaError>),
1206 #[error("Collection [{0}] does not exist")]
1207 NotFound(String),
1208}
1209
1210impl ChromaError for ListCollectionVersionsError {
1211 fn code(&self) -> ErrorCodes {
1212 match self {
1213 ListCollectionVersionsError::Internal(err) => err.code(),
1214 ListCollectionVersionsError::NotFound(_) => ErrorCodes::NotFound,
1215 }
1216 }
1217}
1218
1219pub const CHROMA_KEY: &str = "chroma:";
1222pub const CHROMA_DOCUMENT_KEY: &str = "chroma:document";
1223pub const CHROMA_URI_KEY: &str = "chroma:uri";
1224
1225#[derive(Serialize, Deserialize, Debug, Clone)]
1228#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1229pub struct AddCollectionRecordsPayload {
1230 pub ids: Vec<String>,
1231 pub embeddings: EmbeddingsPayload,
1232 pub documents: Option<Vec<Option<String>>>,
1233 pub uris: Option<Vec<Option<String>>>,
1234 pub metadatas: Option<Vec<Option<Metadata>>>,
1235}
1236
1237impl AddCollectionRecordsPayload {
1238 pub fn new(
1239 ids: Vec<String>,
1240 embeddings: Vec<Vec<f32>>,
1241 documents: Option<Vec<Option<String>>>,
1242 uris: Option<Vec<Option<String>>>,
1243 metadatas: Option<Vec<Option<Metadata>>>,
1244 ) -> Self {
1245 Self {
1246 ids,
1247 embeddings: EmbeddingsPayload::JsonArrays(embeddings),
1248 documents,
1249 uris,
1250 metadatas,
1251 }
1252 }
1253}
1254
1255#[non_exhaustive]
1256#[derive(Debug, Clone, Validate, Serialize)]
1257#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1258pub struct AddCollectionRecordsRequest {
1259 pub tenant_id: String,
1260 pub database_name: String,
1261 pub collection_id: CollectionUuid,
1262 pub ids: Vec<String>,
1263 #[validate(custom(function = "validate_embeddings"))]
1264 pub embeddings: Vec<Vec<f32>>,
1265 pub documents: Option<Vec<Option<String>>>,
1266 pub uris: Option<Vec<Option<String>>>,
1267 #[validate(custom(function = "validate_metadata_vec"))]
1268 pub metadatas: Option<Vec<Option<Metadata>>>,
1269}
1270
1271impl AddCollectionRecordsRequest {
1272 #[allow(clippy::too_many_arguments)]
1273 pub fn try_new(
1274 tenant_id: String,
1275 database_name: String,
1276 collection_id: CollectionUuid,
1277 ids: Vec<String>,
1278 embeddings: Vec<Vec<f32>>,
1279 documents: Option<Vec<Option<String>>>,
1280 uris: Option<Vec<Option<String>>>,
1281 metadatas: Option<Vec<Option<Metadata>>>,
1282 ) -> Result<Self, ChromaValidationError> {
1283 let request = Self {
1284 tenant_id,
1285 database_name,
1286 collection_id,
1287 ids,
1288 embeddings,
1289 documents,
1290 uris,
1291 metadatas,
1292 };
1293 request.validate().map_err(ChromaValidationError::from)?;
1294 Ok(request)
1295 }
1296
1297 pub fn into_payload(self) -> AddCollectionRecordsPayload {
1298 AddCollectionRecordsPayload {
1299 ids: self.ids,
1300 embeddings: EmbeddingsPayload::JsonArrays(self.embeddings),
1301 documents: self.documents,
1302 uris: self.uris,
1303 metadatas: self.metadatas,
1304 }
1305 }
1306}
1307
1308fn validate_embeddings(embeddings: &[Vec<f32>]) -> Result<(), ValidationError> {
1309 if embeddings.iter().any(|e| e.is_empty()) {
1310 return Err(ValidationError::new("embedding_minimum_dimensions")
1311 .with_message("Each embedding must have at least 1 dimension".into()));
1312 }
1313 Ok(())
1314}
1315
1316#[derive(Serialize, Default, Deserialize)]
1317#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1318pub struct AddCollectionRecordsResponse {}
1319
1320#[derive(Error, Debug)]
1321pub enum AddCollectionRecordsError {
1322 #[error("Failed to get collection: {0}")]
1323 Collection(#[from] GetCollectionError),
1324 #[error("Backoff and retry")]
1325 Backoff,
1326 #[error(transparent)]
1327 Other(#[from] Box<dyn ChromaError>),
1328}
1329
1330impl ChromaError for AddCollectionRecordsError {
1331 fn code(&self) -> ErrorCodes {
1332 match self {
1333 AddCollectionRecordsError::Collection(err) => err.code(),
1334 AddCollectionRecordsError::Backoff => ErrorCodes::ResourceExhausted,
1335 AddCollectionRecordsError::Other(err) => err.code(),
1336 }
1337 }
1338}
1339
1340#[derive(Deserialize, Debug, Clone, Serialize)]
1343#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1344pub struct UpdateCollectionRecordsPayload {
1345 pub ids: Vec<String>,
1346 pub embeddings: Option<UpdateEmbeddingsPayload>,
1347 pub documents: Option<Vec<Option<String>>>,
1348 pub uris: Option<Vec<Option<String>>>,
1349 pub metadatas: Option<Vec<Option<UpdateMetadata>>>,
1350}
1351
1352#[non_exhaustive]
1353#[derive(Debug, Clone, Validate, Serialize)]
1354#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1355pub struct UpdateCollectionRecordsRequest {
1356 pub tenant_id: String,
1357 pub database_name: String,
1358 pub collection_id: CollectionUuid,
1359 pub ids: Vec<String>,
1360 pub embeddings: Option<Vec<Option<Vec<f32>>>>,
1361 pub documents: Option<Vec<Option<String>>>,
1362 pub uris: Option<Vec<Option<String>>>,
1363 #[validate(custom(function = "validate_update_metadata_vec"))]
1364 pub metadatas: Option<Vec<Option<UpdateMetadata>>>,
1365}
1366
1367impl UpdateCollectionRecordsRequest {
1368 #[allow(clippy::too_many_arguments)]
1369 pub fn try_new(
1370 tenant_id: String,
1371 database_name: String,
1372 collection_id: CollectionUuid,
1373 ids: Vec<String>,
1374 embeddings: Option<Vec<Option<Vec<f32>>>>,
1375 documents: Option<Vec<Option<String>>>,
1376 uris: Option<Vec<Option<String>>>,
1377 metadatas: Option<Vec<Option<UpdateMetadata>>>,
1378 ) -> Result<Self, ChromaValidationError> {
1379 let request = Self {
1380 tenant_id,
1381 database_name,
1382 collection_id,
1383 ids,
1384 embeddings,
1385 documents,
1386 uris,
1387 metadatas,
1388 };
1389 request.validate().map_err(ChromaValidationError::from)?;
1390 Ok(request)
1391 }
1392
1393 pub fn into_payload(self) -> UpdateCollectionRecordsPayload {
1394 UpdateCollectionRecordsPayload {
1395 ids: self.ids,
1396 embeddings: self.embeddings.map(UpdateEmbeddingsPayload::JsonArrays),
1397 documents: self.documents,
1398 uris: self.uris,
1399 metadatas: self.metadatas,
1400 }
1401 }
1402}
1403
1404#[derive(Serialize, Deserialize)]
1405#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1406pub struct UpdateCollectionRecordsResponse {}
1407
1408#[derive(Error, Debug)]
1409pub enum UpdateCollectionRecordsError {
1410 #[error("Backoff and retry")]
1411 Backoff,
1412 #[error(transparent)]
1413 Other(#[from] Box<dyn ChromaError>),
1414}
1415
1416impl ChromaError for UpdateCollectionRecordsError {
1417 fn code(&self) -> ErrorCodes {
1418 match self {
1419 UpdateCollectionRecordsError::Backoff => ErrorCodes::ResourceExhausted,
1420 UpdateCollectionRecordsError::Other(err) => err.code(),
1421 }
1422 }
1423}
1424
1425#[derive(Deserialize, Debug, Clone, Serialize)]
1428#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1429pub struct UpsertCollectionRecordsPayload {
1430 pub ids: Vec<String>,
1431 pub embeddings: EmbeddingsPayload,
1432 pub documents: Option<Vec<Option<String>>>,
1433 pub uris: Option<Vec<Option<String>>>,
1434 pub metadatas: Option<Vec<Option<UpdateMetadata>>>,
1435}
1436
1437#[non_exhaustive]
1438#[derive(Debug, Clone, Validate, Serialize)]
1439#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1440pub struct UpsertCollectionRecordsRequest {
1441 pub tenant_id: String,
1442 pub database_name: String,
1443 pub collection_id: CollectionUuid,
1444 pub ids: Vec<String>,
1445 #[validate(custom(function = "validate_embeddings"))]
1446 pub embeddings: Vec<Vec<f32>>,
1447 pub documents: Option<Vec<Option<String>>>,
1448 pub uris: Option<Vec<Option<String>>>,
1449 #[validate(custom(function = "validate_update_metadata_vec"))]
1450 pub metadatas: Option<Vec<Option<UpdateMetadata>>>,
1451}
1452
1453impl UpsertCollectionRecordsRequest {
1454 #[allow(clippy::too_many_arguments)]
1455 pub fn try_new(
1456 tenant_id: String,
1457 database_name: String,
1458 collection_id: CollectionUuid,
1459 ids: Vec<String>,
1460 embeddings: Vec<Vec<f32>>,
1461 documents: Option<Vec<Option<String>>>,
1462 uris: Option<Vec<Option<String>>>,
1463 metadatas: Option<Vec<Option<UpdateMetadata>>>,
1464 ) -> Result<Self, ChromaValidationError> {
1465 let request = Self {
1466 tenant_id,
1467 database_name,
1468 collection_id,
1469 ids,
1470 embeddings,
1471 documents,
1472 uris,
1473 metadatas,
1474 };
1475 request.validate().map_err(ChromaValidationError::from)?;
1476 Ok(request)
1477 }
1478
1479 pub fn into_payload(self) -> UpsertCollectionRecordsPayload {
1480 UpsertCollectionRecordsPayload {
1481 ids: self.ids.clone(),
1482 embeddings: EmbeddingsPayload::JsonArrays(self.embeddings.clone()),
1483 documents: self.documents.clone(),
1484 uris: self.uris.clone(),
1485 metadatas: self.metadatas.clone(),
1486 }
1487 }
1488}
1489
1490#[derive(Serialize, Deserialize)]
1491#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1492pub struct UpsertCollectionRecordsResponse {}
1493
1494#[derive(Error, Debug)]
1495pub enum UpsertCollectionRecordsError {
1496 #[error("Backoff and retry")]
1497 Backoff,
1498 #[error(transparent)]
1499 Other(#[from] Box<dyn ChromaError>),
1500}
1501
1502impl ChromaError for UpsertCollectionRecordsError {
1503 fn code(&self) -> ErrorCodes {
1504 match self {
1505 UpsertCollectionRecordsError::Backoff => ErrorCodes::ResourceExhausted,
1506 UpsertCollectionRecordsError::Other(err) => err.code(),
1507 }
1508 }
1509}
1510
1511#[derive(Deserialize, Debug, Clone, Serialize)]
1514#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1515pub struct DeleteCollectionRecordsPayload {
1516 pub ids: Option<Vec<String>>,
1517 #[serde(flatten)]
1518 pub where_fields: RawWhereFields,
1519}
1520
1521#[non_exhaustive]
1522#[derive(Debug, Clone, Validate, Serialize)]
1523#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1524pub struct DeleteCollectionRecordsRequest {
1525 pub tenant_id: String,
1526 pub database_name: String,
1527 pub collection_id: CollectionUuid,
1528 pub ids: Option<Vec<String>>,
1529 pub r#where: Option<Where>,
1530}
1531
1532impl DeleteCollectionRecordsRequest {
1533 pub fn try_new(
1534 tenant_id: String,
1535 database_name: String,
1536 collection_id: CollectionUuid,
1537 ids: Option<Vec<String>>,
1538 r#where: Option<Where>,
1539 ) -> Result<Self, ChromaValidationError> {
1540 if ids.as_ref().map(|ids| ids.is_empty()).unwrap_or(false) && r#where.is_none() {
1541 return Err(ChromaValidationError::from((
1542 ("ids, where"),
1543 ValidationError::new("filter")
1544 .with_message("Either ids or where must be specified".into()),
1545 )));
1546 }
1547
1548 let request = Self {
1549 tenant_id,
1550 database_name,
1551 collection_id,
1552 ids,
1553 r#where,
1554 };
1555 request.validate().map_err(ChromaValidationError::from)?;
1556 Ok(request)
1557 }
1558
1559 pub fn into_payload(self) -> Result<DeleteCollectionRecordsPayload, WhereError> {
1560 let where_fields = if let Some(r#where) = self.r#where.as_ref() {
1561 RawWhereFields::from_json_str(Some(&serde_json::to_string(r#where)?), None)?
1562 } else {
1563 RawWhereFields::default()
1564 };
1565 Ok(DeleteCollectionRecordsPayload {
1566 ids: self.ids.clone(),
1567 where_fields,
1568 })
1569 }
1570}
1571
1572#[derive(Serialize, Deserialize)]
1573#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1574pub struct DeleteCollectionRecordsResponse {}
1575
1576#[derive(Error, Debug)]
1577pub enum DeleteCollectionRecordsError {
1578 #[error("Failed to resolve records for deletion: {0}")]
1579 Get(#[from] ExecutorError),
1580 #[error("Backoff and retry")]
1581 Backoff,
1582 #[error(transparent)]
1583 Internal(#[from] Box<dyn ChromaError>),
1584}
1585
1586impl ChromaError for DeleteCollectionRecordsError {
1587 fn code(&self) -> ErrorCodes {
1588 match self {
1589 DeleteCollectionRecordsError::Get(err) => err.code(),
1590 DeleteCollectionRecordsError::Backoff => ErrorCodes::ResourceExhausted,
1591 DeleteCollectionRecordsError::Internal(err) => err.code(),
1592 }
1593 }
1594}
1595
1596#[derive(Error, Debug)]
1599#[error("Invalid include value: {0}")]
1600pub struct IncludeParsingError(String);
1601
1602impl ChromaError for IncludeParsingError {
1603 fn code(&self) -> ErrorCodes {
1604 ErrorCodes::InvalidArgument
1605 }
1606}
1607
1608#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
1609#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1610pub enum Include {
1611 #[serde(rename = "distances")]
1612 Distance,
1613 #[serde(rename = "documents")]
1614 Document,
1615 #[serde(rename = "embeddings")]
1616 Embedding,
1617 #[serde(rename = "metadatas")]
1618 Metadata,
1619 #[serde(rename = "uris")]
1620 Uri,
1621}
1622
1623impl TryFrom<&str> for Include {
1624 type Error = IncludeParsingError;
1625
1626 fn try_from(value: &str) -> Result<Self, Self::Error> {
1627 match value {
1628 "distances" => Ok(Include::Distance),
1629 "documents" => Ok(Include::Document),
1630 "embeddings" => Ok(Include::Embedding),
1631 "metadatas" => Ok(Include::Metadata),
1632 "uris" => Ok(Include::Uri),
1633 _ => Err(IncludeParsingError(value.to_string())),
1634 }
1635 }
1636}
1637
1638#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
1639#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1640#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
1641pub struct IncludeList(pub Vec<Include>);
1642
1643impl IncludeList {
1644 pub fn empty() -> Self {
1645 Self(Vec::new())
1646 }
1647
1648 pub fn default_query() -> Self {
1649 Self(vec![
1650 Include::Document,
1651 Include::Metadata,
1652 Include::Distance,
1653 ])
1654 }
1655 pub fn default_get() -> Self {
1656 Self(vec![Include::Document, Include::Metadata])
1657 }
1658 pub fn all() -> Self {
1659 Self(vec![
1660 Include::Document,
1661 Include::Metadata,
1662 Include::Distance,
1663 Include::Embedding,
1664 Include::Uri,
1665 ])
1666 }
1667}
1668
1669impl TryFrom<Vec<String>> for IncludeList {
1670 type Error = IncludeParsingError;
1671
1672 fn try_from(value: Vec<String>) -> Result<Self, Self::Error> {
1673 let mut includes = Vec::new();
1674 for v in value {
1675 if v == "data" {
1677 includes.push(Include::Metadata);
1678 continue;
1679 }
1680
1681 includes.push(Include::try_from(v.as_str())?);
1682 }
1683 Ok(IncludeList(includes))
1684 }
1685}
1686
1687#[non_exhaustive]
1690#[derive(Clone, Deserialize, Serialize, Validate)]
1691pub struct CountRequest {
1692 pub tenant_id: String,
1693 pub database_name: String,
1694 pub collection_id: CollectionUuid,
1695}
1696
1697impl CountRequest {
1698 pub fn try_new(
1699 tenant_id: String,
1700 database_name: String,
1701 collection_id: CollectionUuid,
1702 ) -> Result<Self, ChromaValidationError> {
1703 let request = Self {
1704 tenant_id,
1705 database_name,
1706 collection_id,
1707 };
1708 request.validate().map_err(ChromaValidationError::from)?;
1709 Ok(request)
1710 }
1711}
1712
1713pub type CountResponse = u32;
1714
1715#[derive(Debug, thiserror::Error)]
1718pub enum WhereError {
1719 #[error("serialization: {0}")]
1720 Serialization(#[from] serde_json::Error),
1721 #[error("validation: {0}")]
1722 Validation(#[from] WhereValidationError),
1723}
1724
1725#[derive(Debug, Clone, Deserialize, Serialize)]
1728#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1729pub struct GetRequestPayload {
1730 pub ids: Option<Vec<String>>,
1731 #[serde(flatten)]
1732 pub where_fields: RawWhereFields,
1733 pub limit: Option<u32>,
1734 pub offset: Option<u32>,
1735 #[serde(default = "IncludeList::default_get")]
1736 pub include: IncludeList,
1737}
1738
1739#[non_exhaustive]
1740#[derive(Debug, Clone, Validate, Serialize)]
1741#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1742pub struct GetRequest {
1743 pub tenant_id: String,
1744 pub database_name: String,
1745 pub collection_id: CollectionUuid,
1746 pub ids: Option<Vec<String>>,
1747 pub r#where: Option<Where>,
1748 pub limit: Option<u32>,
1749 pub offset: u32,
1750 pub include: IncludeList,
1751}
1752
1753impl GetRequest {
1754 #[allow(clippy::too_many_arguments)]
1755 pub fn try_new(
1756 tenant_id: String,
1757 database_name: String,
1758 collection_id: CollectionUuid,
1759 ids: Option<Vec<String>>,
1760 r#where: Option<Where>,
1761 limit: Option<u32>,
1762 offset: u32,
1763 include: IncludeList,
1764 ) -> Result<Self, ChromaValidationError> {
1765 let request = Self {
1766 tenant_id,
1767 database_name,
1768 collection_id,
1769 ids,
1770 r#where,
1771 limit,
1772 offset,
1773 include,
1774 };
1775 request.validate().map_err(ChromaValidationError::from)?;
1776 Ok(request)
1777 }
1778
1779 pub fn into_payload(self) -> Result<GetRequestPayload, WhereError> {
1780 let where_fields = if let Some(r#where) = self.r#where.as_ref() {
1781 RawWhereFields::from_json_str(Some(&serde_json::to_string(r#where)?), None)?
1782 } else {
1783 RawWhereFields::default()
1784 };
1785 Ok(GetRequestPayload {
1786 ids: self.ids,
1787 where_fields,
1788 limit: self.limit,
1789 offset: Some(self.offset),
1790 include: self.include,
1791 })
1792 }
1793}
1794
1795#[derive(Clone, Deserialize, Serialize, Debug, Default)]
1796#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1797#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
1798pub struct GetResponse {
1799 pub ids: Vec<String>,
1800 pub embeddings: Option<Vec<Vec<f32>>>,
1801 pub documents: Option<Vec<Option<String>>>,
1802 pub uris: Option<Vec<Option<String>>>,
1803 pub metadatas: Option<Vec<Option<Metadata>>>,
1805 pub include: Vec<Include>,
1806}
1807
1808impl GetResponse {
1809 pub fn sort_by_ids(&mut self) {
1810 let mut indices: Vec<usize> = (0..self.ids.len()).collect();
1811 indices.sort_by(|&a, &b| self.ids[a].cmp(&self.ids[b]));
1812
1813 let sorted_ids = indices.iter().map(|&i| self.ids[i].clone()).collect();
1814 self.ids = sorted_ids;
1815
1816 if let Some(ref mut embeddings) = self.embeddings {
1817 let sorted_embeddings = indices.iter().map(|&i| embeddings[i].clone()).collect();
1818 *embeddings = sorted_embeddings;
1819 }
1820
1821 if let Some(ref mut documents) = self.documents {
1822 let sorted_docs = indices.iter().map(|&i| documents[i].clone()).collect();
1823 *documents = sorted_docs;
1824 }
1825
1826 if let Some(ref mut uris) = self.uris {
1827 let sorted_uris = indices.iter().map(|&i| uris[i].clone()).collect();
1828 *uris = sorted_uris;
1829 }
1830
1831 if let Some(ref mut metadatas) = self.metadatas {
1832 let sorted_metas = indices.iter().map(|&i| metadatas[i].clone()).collect();
1833 *metadatas = sorted_metas;
1834 }
1835 }
1836}
1837
1838#[cfg(feature = "pyo3")]
1839#[pyo3::pymethods]
1840impl GetResponse {
1841 #[getter]
1842 pub fn ids(&self) -> &Vec<String> {
1843 &self.ids
1844 }
1845
1846 #[getter]
1847 pub fn embeddings(&self) -> Option<Vec<Vec<f32>>> {
1848 self.embeddings.clone()
1849 }
1850
1851 #[getter]
1852 pub fn documents(&self) -> Option<Vec<Option<String>>> {
1853 self.documents.clone()
1854 }
1855
1856 #[getter]
1857 pub fn uris(&self) -> Option<Vec<Option<String>>> {
1858 self.uris.clone()
1859 }
1860
1861 #[getter]
1862 pub fn metadatas(&self) -> Option<Vec<Option<Metadata>>> {
1863 self.metadatas.clone()
1864 }
1865}
1866
1867impl From<(GetResult, IncludeList)> for GetResponse {
1868 fn from((result, IncludeList(include_vec)): (GetResult, IncludeList)) -> Self {
1869 let mut res = Self {
1870 ids: Vec::new(),
1871 embeddings: include_vec
1872 .contains(&Include::Embedding)
1873 .then_some(Vec::new()),
1874 documents: include_vec
1875 .contains(&Include::Document)
1876 .then_some(Vec::new()),
1877 uris: include_vec.contains(&Include::Uri).then_some(Vec::new()),
1878 metadatas: include_vec
1879 .contains(&Include::Metadata)
1880 .then_some(Vec::new()),
1881 include: include_vec,
1882 };
1883 for ProjectionRecord {
1884 id,
1885 document,
1886 embedding,
1887 mut metadata,
1888 } in result.result.records
1889 {
1890 res.ids.push(id);
1891 if let (Some(emb), Some(embeddings)) = (embedding, res.embeddings.as_mut()) {
1892 embeddings.push(emb);
1893 }
1894 if let Some(documents) = res.documents.as_mut() {
1895 documents.push(document);
1896 }
1897 let uri = metadata.as_mut().and_then(|meta| {
1898 meta.remove(CHROMA_URI_KEY).and_then(|v| {
1899 if let crate::MetadataValue::Str(uri) = v {
1900 Some(uri)
1901 } else {
1902 None
1903 }
1904 })
1905 });
1906 if let Some(uris) = res.uris.as_mut() {
1907 uris.push(uri);
1908 }
1909
1910 let metadata = metadata.map(|m| {
1911 m.into_iter()
1912 .filter(|(k, _)| !k.starts_with(CHROMA_KEY))
1913 .collect()
1914 });
1915 if let Some(metadatas) = res.metadatas.as_mut() {
1916 metadatas.push(metadata);
1917 }
1918 }
1919 res
1920 }
1921}
1922
1923#[derive(Deserialize, Debug, Clone, Serialize)]
1926#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1927pub struct QueryRequestPayload {
1928 pub ids: Option<Vec<String>>,
1929 #[serde(flatten)]
1930 pub where_fields: RawWhereFields,
1931 pub query_embeddings: Vec<Vec<f32>>,
1932 pub n_results: Option<u32>,
1933 #[serde(default = "IncludeList::default_query")]
1934 pub include: IncludeList,
1935}
1936
1937#[non_exhaustive]
1938#[derive(Debug, Clone, Validate, Serialize)]
1939#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1940pub struct QueryRequest {
1941 pub tenant_id: String,
1942 pub database_name: String,
1943 pub collection_id: CollectionUuid,
1944 pub ids: Option<Vec<String>>,
1945 pub r#where: Option<Where>,
1946 pub embeddings: Vec<Vec<f32>>,
1947 pub n_results: u32,
1948 pub include: IncludeList,
1949}
1950
1951impl QueryRequest {
1952 #[allow(clippy::too_many_arguments)]
1953 pub fn try_new(
1954 tenant_id: String,
1955 database_name: String,
1956 collection_id: CollectionUuid,
1957 ids: Option<Vec<String>>,
1958 r#where: Option<Where>,
1959 embeddings: Vec<Vec<f32>>,
1960 n_results: u32,
1961 include: IncludeList,
1962 ) -> Result<Self, ChromaValidationError> {
1963 let request = Self {
1964 tenant_id,
1965 database_name,
1966 collection_id,
1967 ids,
1968 r#where,
1969 embeddings,
1970 n_results,
1971 include,
1972 };
1973 request.validate().map_err(ChromaValidationError::from)?;
1974 Ok(request)
1975 }
1976
1977 pub fn into_payload(self) -> Result<QueryRequestPayload, WhereError> {
1978 let where_fields = if let Some(r#where) = self.r#where.as_ref() {
1979 RawWhereFields::from_json_str(Some(&serde_json::to_string(r#where)?), None)?
1980 } else {
1981 RawWhereFields::default()
1982 };
1983 Ok(QueryRequestPayload {
1984 ids: self.ids,
1985 where_fields,
1986 query_embeddings: self.embeddings,
1987 n_results: Some(self.n_results),
1988 include: self.include,
1989 })
1990 }
1991}
1992
1993#[derive(Clone, Deserialize, Serialize, Debug)]
1994#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
1995#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
1996pub struct QueryResponse {
1997 pub ids: Vec<Vec<String>>,
1998 pub embeddings: Option<Vec<Vec<Option<Vec<f32>>>>>,
1999 pub documents: Option<Vec<Vec<Option<String>>>>,
2000 pub uris: Option<Vec<Vec<Option<String>>>>,
2001 pub metadatas: Option<Vec<Vec<Option<Metadata>>>>,
2002 pub distances: Option<Vec<Vec<Option<f32>>>>,
2003 pub include: Vec<Include>,
2004}
2005
2006impl QueryResponse {
2007 pub fn sort_by_ids(&mut self) {
2008 fn reorder<T: Clone>(v: &mut [T], indices: &[usize]) {
2009 let old = v.to_owned();
2010 for (new_pos, &i) in indices.iter().enumerate() {
2011 v[new_pos] = old[i].clone();
2012 }
2013 }
2014
2015 for i in 0..self.ids.len() {
2016 let mut indices: Vec<usize> = (0..self.ids[i].len()).collect();
2017
2018 indices.sort_unstable_by(|&a, &b| self.ids[i][a].cmp(&self.ids[i][b]));
2019
2020 reorder(&mut self.ids[i], &indices);
2021
2022 if let Some(embeddings) = &mut self.embeddings {
2023 reorder(&mut embeddings[i], &indices);
2024 }
2025
2026 if let Some(documents) = &mut self.documents {
2027 reorder(&mut documents[i], &indices);
2028 }
2029
2030 if let Some(uris) = &mut self.uris {
2031 reorder(&mut uris[i], &indices);
2032 }
2033
2034 if let Some(metadatas) = &mut self.metadatas {
2035 reorder(&mut metadatas[i], &indices);
2036 }
2037
2038 if let Some(distances) = &mut self.distances {
2039 reorder(&mut distances[i], &indices);
2040 }
2041 }
2042 }
2043}
2044
2045#[cfg(feature = "pyo3")]
2046#[pyo3::pymethods]
2047impl QueryResponse {
2048 #[getter]
2049 pub fn ids(&self) -> &Vec<Vec<String>> {
2050 &self.ids
2051 }
2052
2053 #[getter]
2054 pub fn embeddings(&self) -> Option<Vec<Vec<Option<Vec<f32>>>>> {
2055 self.embeddings.clone()
2056 }
2057
2058 #[getter]
2059 pub fn documents(&self) -> Option<Vec<Vec<Option<String>>>> {
2060 self.documents.clone()
2061 }
2062
2063 #[getter]
2064 pub fn uris(&self) -> Option<Vec<Vec<Option<String>>>> {
2065 self.uris.clone()
2066 }
2067
2068 #[getter]
2069 pub fn metadatas(&self) -> Option<Vec<Vec<Option<Metadata>>>> {
2070 self.metadatas.clone()
2071 }
2072
2073 #[getter]
2074 pub fn distances(&self) -> Option<Vec<Vec<Option<f32>>>> {
2075 self.distances.clone()
2076 }
2077}
2078
2079impl From<(KnnBatchResult, IncludeList)> for QueryResponse {
2080 fn from((result, IncludeList(include_vec)): (KnnBatchResult, IncludeList)) -> Self {
2081 let mut res = Self {
2082 ids: Vec::new(),
2083 embeddings: include_vec
2084 .contains(&Include::Embedding)
2085 .then_some(Vec::new()),
2086 documents: include_vec
2087 .contains(&Include::Document)
2088 .then_some(Vec::new()),
2089 uris: include_vec.contains(&Include::Uri).then_some(Vec::new()),
2090 metadatas: include_vec
2091 .contains(&Include::Metadata)
2092 .then_some(Vec::new()),
2093 distances: include_vec
2094 .contains(&Include::Distance)
2095 .then_some(Vec::new()),
2096 include: include_vec,
2097 };
2098 for query_result in result.results {
2099 let mut ids = Vec::new();
2100 let mut embeddings = Vec::new();
2101 let mut documents = Vec::new();
2102 let mut uris = Vec::new();
2103 let mut metadatas = Vec::new();
2104 let mut distances = Vec::new();
2105 for KnnProjectionRecord {
2106 record:
2107 ProjectionRecord {
2108 id,
2109 document,
2110 embedding,
2111 mut metadata,
2112 },
2113 distance,
2114 } in query_result.records
2115 {
2116 ids.push(id);
2117 embeddings.push(embedding);
2118 documents.push(document);
2119
2120 let uri = metadata.as_mut().and_then(|meta| {
2121 meta.remove(CHROMA_URI_KEY).and_then(|v| {
2122 if let crate::MetadataValue::Str(uri) = v {
2123 Some(uri)
2124 } else {
2125 None
2126 }
2127 })
2128 });
2129 uris.push(uri);
2130
2131 let metadata = metadata.map(|m| {
2132 m.into_iter()
2133 .filter(|(k, _)| !k.starts_with(CHROMA_KEY))
2134 .collect()
2135 });
2136 metadatas.push(metadata);
2137
2138 distances.push(distance);
2139 }
2140 res.ids.push(ids);
2141
2142 if let Some(res_embs) = res.embeddings.as_mut() {
2143 res_embs.push(embeddings);
2144 }
2145 if let Some(res_docs) = res.documents.as_mut() {
2146 res_docs.push(documents);
2147 }
2148 if let Some(res_uri) = res.uris.as_mut() {
2149 res_uri.push(uris);
2150 }
2151 if let Some(res_metas) = res.metadatas.as_mut() {
2152 res_metas.push(metadatas);
2153 }
2154 if let Some(res_dists) = res.distances.as_mut() {
2155 res_dists.push(distances);
2156 }
2157 }
2158 res
2159 }
2160}
2161
2162#[derive(Debug, Clone, Deserialize, Serialize)]
2163#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
2164pub struct SearchRequestPayload {
2165 pub searches: Vec<SearchPayload>,
2166 #[serde(default)]
2169 pub read_level: ReadLevel,
2170}
2171
2172#[non_exhaustive]
2173#[derive(Clone, Debug, Serialize, Validate)]
2174#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
2175pub struct SearchRequest {
2176 pub tenant_id: String,
2177 pub database_name: String,
2178 pub collection_id: CollectionUuid,
2179 #[validate(nested)]
2180 pub searches: Vec<SearchPayload>,
2181 pub read_level: ReadLevel,
2183}
2184
2185impl SearchRequest {
2186 pub fn try_new(
2187 tenant_id: String,
2188 database_name: String,
2189 collection_id: CollectionUuid,
2190 searches: Vec<SearchPayload>,
2191 read_level: ReadLevel,
2192 ) -> Result<Self, ChromaValidationError> {
2193 let request = Self {
2194 tenant_id,
2195 database_name,
2196 collection_id,
2197 searches,
2198 read_level,
2199 };
2200 request.validate().map_err(ChromaValidationError::from)?;
2201 Ok(request)
2202 }
2203
2204 pub fn into_payload(self) -> SearchRequestPayload {
2205 SearchRequestPayload {
2206 searches: self.searches,
2207 read_level: self.read_level,
2208 }
2209 }
2210}
2211
2212#[derive(Clone, Deserialize, Serialize, Debug)]
2213#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
2214pub struct SearchResponse {
2215 pub ids: Vec<Vec<String>>,
2216 pub documents: Vec<Option<Vec<Option<String>>>>,
2217 pub embeddings: Vec<Option<Vec<Option<Vec<f32>>>>>,
2218 pub metadatas: Vec<Option<Vec<Option<Metadata>>>>,
2219 pub scores: Vec<Option<Vec<Option<f32>>>>,
2220 pub select: Vec<Vec<Key>>,
2221}
2222
2223impl From<(SearchResult, Vec<SearchPayload>)> for SearchResponse {
2224 fn from((result, payloads): (SearchResult, Vec<SearchPayload>)) -> Self {
2225 let num_payloads = payloads.len();
2226 let mut res = Self {
2227 ids: Vec::with_capacity(num_payloads),
2228 documents: Vec::with_capacity(num_payloads),
2229 embeddings: Vec::with_capacity(num_payloads),
2230 metadatas: Vec::with_capacity(num_payloads),
2231 scores: Vec::with_capacity(num_payloads),
2232 select: Vec::with_capacity(num_payloads),
2233 };
2234
2235 for (payload_result, payload) in result.results.into_iter().zip(payloads) {
2236 let mut payload_select = Vec::from_iter(payload.select.keys.iter().cloned());
2238 payload_select.sort();
2239
2240 let num_records = payload_result.records.len();
2241 let mut ids = Vec::with_capacity(num_records);
2242 let mut documents = Vec::with_capacity(num_records);
2243 let mut embeddings = Vec::with_capacity(num_records);
2244 let mut metadatas = Vec::with_capacity(num_records);
2245 let mut scores = Vec::with_capacity(num_records);
2246
2247 for record in payload_result.records {
2248 ids.push(record.id);
2249 documents.push(record.document);
2250 embeddings.push(record.embedding);
2251 metadatas.push(record.metadata);
2252 scores.push(record.score);
2253 }
2254
2255 res.ids.push(ids);
2256 res.select.push(payload_select.clone());
2257
2258 res.documents.push(
2260 payload_select
2261 .binary_search(&Key::Document)
2262 .is_ok()
2263 .then_some(documents),
2264 );
2265
2266 res.embeddings.push(
2268 payload_select
2269 .binary_search(&Key::Embedding)
2270 .is_ok()
2271 .then_some(embeddings),
2272 );
2273
2274 let has_metadata = payload_select.binary_search(&Key::Metadata).is_ok()
2277 || payload_select
2278 .last()
2279 .is_some_and(|field| matches!(field, Key::MetadataField(_)));
2280 res.metadatas.push(has_metadata.then_some(metadatas));
2281
2282 res.scores.push(
2284 payload_select
2285 .binary_search(&Key::Score)
2286 .is_ok()
2287 .then_some(scores),
2288 );
2289 }
2290
2291 res
2292 }
2293}
2294
2295#[derive(Error, Debug)]
2296pub enum QueryError {
2297 #[error("Error executing plan: {0}")]
2298 Executor(#[from] ExecutorError),
2299 #[error(transparent)]
2300 Other(#[from] Box<dyn ChromaError>),
2301}
2302
2303impl ChromaError for QueryError {
2304 fn code(&self) -> ErrorCodes {
2305 match self {
2306 QueryError::Executor(e) => e.code(),
2307 QueryError::Other(err) => err.code(),
2308 }
2309 }
2310}
2311
2312#[derive(Serialize)]
2313#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
2314pub struct HealthCheckResponse {
2315 pub is_executor_ready: bool,
2316 pub is_log_client_ready: bool,
2317}
2318
2319impl HealthCheckResponse {
2320 pub fn get_status_code(&self) -> tonic::Code {
2321 if self.is_executor_ready && self.is_log_client_ready {
2322 tonic::Code::Ok
2323 } else {
2324 tonic::Code::Unavailable
2325 }
2326 }
2327}
2328
2329#[derive(Debug, Error)]
2330pub enum ExecutorError {
2331 #[error("Error converting: {0}")]
2332 Conversion(#[from] QueryConversionError),
2333 #[error("Error converting plan to proto: {0}")]
2334 PlanToProto(#[from] PlanToProtoError),
2335 #[error(transparent)]
2336 Grpc(#[from] Status),
2337 #[error("Inconsistent data")]
2338 InconsistentData,
2339 #[error("Collection is missing HNSW configuration")]
2340 CollectionMissingHnswConfiguration,
2341 #[error("Internal error: {0}")]
2342 Internal(Box<dyn ChromaError>),
2343 #[error("Error sending backfill request to compactor: {0}")]
2344 BackfillError(Box<dyn ChromaError>),
2345 #[error("Not implemented: {0}")]
2346 NotImplemented(String),
2347}
2348
2349impl ChromaError for ExecutorError {
2350 fn code(&self) -> ErrorCodes {
2351 match self {
2352 ExecutorError::Conversion(_) => ErrorCodes::InvalidArgument,
2353 ExecutorError::PlanToProto(_) => ErrorCodes::Internal,
2354 ExecutorError::Grpc(e) => e.code().into(),
2355 ExecutorError::InconsistentData => ErrorCodes::Internal,
2356 ExecutorError::CollectionMissingHnswConfiguration => ErrorCodes::Internal,
2357 ExecutorError::Internal(e) => e.code(),
2358 ExecutorError::BackfillError(e) => e.code(),
2359 ExecutorError::NotImplemented(_) => ErrorCodes::Unimplemented,
2360 }
2361 }
2362}
2363
2364#[non_exhaustive]
2367#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
2368#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
2369pub struct AttachFunctionRequest {
2370 #[validate(length(min = 1))]
2371 pub name: String,
2372 pub function_id: String,
2373 pub output_collection: String,
2374 #[serde(default = "default_empty_json_object")]
2375 pub params: serde_json::Value,
2376}
2377
2378fn default_empty_json_object() -> serde_json::Value {
2379 serde_json::json!({})
2380}
2381
2382impl AttachFunctionRequest {
2383 pub fn try_new(
2384 name: String,
2385 function_id: String,
2386 output_collection: String,
2387 params: serde_json::Value,
2388 ) -> Result<Self, ChromaValidationError> {
2389 let request = Self {
2390 name,
2391 function_id,
2392 output_collection,
2393 params,
2394 };
2395 request.validate().map_err(ChromaValidationError::from)?;
2396 Ok(request)
2397 }
2398}
2399
2400#[derive(Clone, Debug, Serialize)]
2401#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
2402pub struct AttachedFunctionInfo {
2403 pub id: String,
2404 pub name: String,
2405 pub function_name: String,
2406}
2407
2408#[derive(Clone, Debug, Serialize)]
2409#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
2410pub struct AttachFunctionResponse {
2411 pub attached_function: AttachedFunctionInfo,
2412 pub created: bool,
2414}
2415
2416#[derive(Clone, Debug, Serialize)]
2418#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
2419pub struct AttachedFunctionApiResponse {
2420 pub id: AttachedFunctionUuid,
2422 pub name: String,
2424 pub function_name: String,
2426 pub input_collection_id: CollectionUuid,
2428 #[serde(rename = "output_collection")]
2430 pub output_collection_name: String,
2431 pub output_collection_id: Option<CollectionUuid>,
2433 pub params: Option<String>,
2435 pub tenant_id: String,
2437 pub database_id: String,
2439 pub completion_offset: u64,
2441 pub min_records_for_invocation: u64,
2443}
2444
2445impl AttachedFunctionApiResponse {
2446 pub fn from_attached_function(af: AttachedFunction) -> Result<Self, GetAttachedFunctionError> {
2448 let function_name = match af.function_id {
2449 id if id == FUNCTION_RECORD_COUNTER_ID => FUNCTION_RECORD_COUNTER_NAME.to_string(),
2450 id if id == FUNCTION_STATISTICS_ID => FUNCTION_STATISTICS_NAME.to_string(),
2451 _ => {
2452 return Err(GetAttachedFunctionError::UnknownFunctionId(af.function_id));
2453 }
2454 };
2455
2456 Ok(Self {
2457 id: af.id,
2458 name: af.name,
2459 function_name,
2460 input_collection_id: af.input_collection_id,
2461 output_collection_name: af.output_collection_name,
2462 output_collection_id: af.output_collection_id,
2463 params: af.params,
2464 tenant_id: af.tenant_id,
2465 database_id: af.database_id,
2466 completion_offset: af.completion_offset,
2467 min_records_for_invocation: af.min_records_for_invocation,
2468 })
2469 }
2470}
2471
2472#[derive(Clone, Debug, Serialize)]
2473#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
2474pub struct GetAttachedFunctionResponse {
2475 pub attached_function: AttachedFunctionApiResponse,
2476}
2477
2478#[derive(Error, Debug)]
2479pub enum AttachFunctionError {
2480 #[error("{0}")]
2481 AlreadyExists(String),
2482 #[error("{0}")]
2483 CollectionAlreadyHasFunction(String),
2484 #[error("Failed to get collection and segments")]
2485 GetCollectionError(#[from] GetCollectionError),
2486 #[error("Input collection [{0}] does not exist")]
2487 InputCollectionNotFound(String),
2488 #[error("Output collection [{0}] already exists")]
2489 OutputCollectionExists(String),
2490 #[error("{0}")]
2491 InvalidArgument(String),
2492 #[error("{0}")]
2493 FunctionNotFound(String),
2494 #[error(transparent)]
2495 Validation(#[from] ChromaValidationError),
2496 #[error(transparent)]
2497 FinishCreate(#[from] crate::FinishCreateAttachedFunctionError),
2498 #[error(transparent)]
2499 Internal(#[from] Box<dyn ChromaError>),
2500}
2501
2502impl ChromaError for AttachFunctionError {
2503 fn code(&self) -> ErrorCodes {
2504 match self {
2505 AttachFunctionError::AlreadyExists(_) => ErrorCodes::AlreadyExists,
2506 AttachFunctionError::CollectionAlreadyHasFunction(_) => ErrorCodes::FailedPrecondition,
2507 AttachFunctionError::GetCollectionError(err) => err.code(),
2508 AttachFunctionError::InputCollectionNotFound(_) => ErrorCodes::NotFound,
2509 AttachFunctionError::OutputCollectionExists(_) => ErrorCodes::AlreadyExists,
2510 AttachFunctionError::InvalidArgument(_) => ErrorCodes::InvalidArgument,
2511 AttachFunctionError::FunctionNotFound(_) => ErrorCodes::NotFound,
2512 AttachFunctionError::Validation(err) => err.code(),
2513 AttachFunctionError::FinishCreate(err) => err.code(),
2514 AttachFunctionError::Internal(err) => err.code(),
2515 }
2516 }
2517}
2518
2519#[derive(Error, Debug)]
2520pub enum GetAttachedFunctionError {
2521 #[error("Attached Function not found")]
2522 NotFound(String),
2523 #[error("Unknown function ID [{0}]. Function may not be registered in the system.")]
2524 UnknownFunctionId(Uuid),
2525 #[error(transparent)]
2526 Internal(#[from] Box<dyn ChromaError>),
2527}
2528
2529impl ChromaError for GetAttachedFunctionError {
2530 fn code(&self) -> ErrorCodes {
2531 match self {
2532 GetAttachedFunctionError::NotFound(_) => ErrorCodes::NotFound,
2533 GetAttachedFunctionError::UnknownFunctionId(_) => ErrorCodes::Internal,
2534 GetAttachedFunctionError::Internal(err) => err.code(),
2535 }
2536 }
2537}
2538
2539#[non_exhaustive]
2540#[derive(Clone, Debug, Deserialize, Validate, Serialize)]
2541#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
2542pub struct DetachFunctionRequest {
2543 #[serde(default)]
2545 pub delete_output: bool,
2546}
2547
2548impl DetachFunctionRequest {
2549 pub fn try_new(delete_output: bool) -> Result<Self, ChromaValidationError> {
2550 let request = Self { delete_output };
2551 request.validate().map_err(ChromaValidationError::from)?;
2552 Ok(request)
2553 }
2554}
2555
2556#[derive(Clone, Debug, Serialize)]
2557#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
2558pub struct DetachFunctionResponse {
2559 pub success: bool,
2560}
2561
2562#[derive(Error, Debug)]
2563pub enum DetachFunctionError {
2564 #[error(" Attached Function with ID [{0}] does not exist")]
2565 NotFound(String),
2566 #[error(transparent)]
2567 Validation(#[from] ChromaValidationError),
2568 #[error(transparent)]
2569 Internal(#[from] Box<dyn ChromaError>),
2570}
2571
2572impl ChromaError for DetachFunctionError {
2573 fn code(&self) -> ErrorCodes {
2574 match self {
2575 DetachFunctionError::NotFound(_) => ErrorCodes::NotFound,
2576 DetachFunctionError::Validation(err) => err.code(),
2577 DetachFunctionError::Internal(err) => err.code(),
2578 }
2579 }
2580}
2581
2582#[cfg(test)]
2583mod test {
2584 use super::*;
2585 use crate::{MetadataValue, SparseVector, UpdateMetadataValue};
2586 use std::collections::HashMap;
2587
2588 #[test]
2589 fn test_create_database_min_length() {
2590 let request = CreateDatabaseRequest::try_new("default_tenant".to_string(), "a".to_string());
2591 assert!(request.is_err());
2592 }
2593
2594 #[test]
2595 fn test_create_tenant_min_length() {
2596 let request = CreateTenantRequest::try_new("a".to_string());
2597 assert!(request.is_err());
2598 }
2599
2600 #[test]
2601 fn test_add_request_validates_sparse_vectors() {
2602 let mut metadata = HashMap::new();
2603 metadata.insert(
2605 "sparse".to_string(),
2606 MetadataValue::SparseVector(
2607 SparseVector::new(vec![3, 1, 2], vec![0.3, 0.1, 0.2]).unwrap(),
2608 ),
2609 );
2610
2611 let result = AddCollectionRecordsRequest::try_new(
2612 "tenant".to_string(),
2613 "database".to_string(),
2614 CollectionUuid(uuid::Uuid::new_v4()),
2615 vec!["id1".to_string()],
2616 vec![vec![0.1, 0.2]],
2617 None,
2618 None,
2619 Some(vec![Some(metadata)]),
2620 );
2621
2622 assert!(result.is_err());
2624 }
2625
2626 #[test]
2627 fn test_update_request_validates_sparse_vectors() {
2628 let mut metadata = HashMap::new();
2629 metadata.insert(
2631 "sparse".to_string(),
2632 UpdateMetadataValue::SparseVector(
2633 SparseVector::new(vec![3, 1, 2], vec![0.3, 0.1, 0.2]).unwrap(),
2634 ),
2635 );
2636
2637 let result = UpdateCollectionRecordsRequest::try_new(
2638 "tenant".to_string(),
2639 "database".to_string(),
2640 CollectionUuid(uuid::Uuid::new_v4()),
2641 vec!["id1".to_string()],
2642 None,
2643 None,
2644 None,
2645 Some(vec![Some(metadata)]),
2646 );
2647
2648 assert!(result.is_err());
2650 }
2651
2652 #[test]
2653 fn test_upsert_request_validates_sparse_vectors() {
2654 let mut metadata = HashMap::new();
2655 metadata.insert(
2657 "sparse".to_string(),
2658 UpdateMetadataValue::SparseVector(
2659 SparseVector::new(vec![3, 1, 2], vec![0.3, 0.1, 0.2]).unwrap(),
2660 ),
2661 );
2662
2663 let result = UpsertCollectionRecordsRequest::try_new(
2664 "tenant".to_string(),
2665 "database".to_string(),
2666 CollectionUuid(uuid::Uuid::new_v4()),
2667 vec!["id1".to_string()],
2668 vec![vec![0.1, 0.2]],
2669 None,
2670 None,
2671 Some(vec![Some(metadata)]),
2672 );
2673
2674 assert!(result.is_err());
2676 }
2677}