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