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