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