diff --git a/crates/dump/src/lib.rs b/crates/dump/src/lib.rs index a2b72e0e5..fdbd701be 100644 --- a/crates/dump/src/lib.rs +++ b/crates/dump/src/lib.rs @@ -331,6 +331,7 @@ pub(crate) mod test { facet_search: Setting::NotSet, prefix_search: Setting::NotSet, chat: Setting::NotSet, + vector_store: Setting::NotSet, _kind: std::marker::PhantomData, }; settings.check() diff --git a/crates/dump/src/reader/compat/v5_to_v6.rs b/crates/dump/src/reader/compat/v5_to_v6.rs index cdfa6847e..8261746e0 100644 --- a/crates/dump/src/reader/compat/v5_to_v6.rs +++ b/crates/dump/src/reader/compat/v5_to_v6.rs @@ -421,6 +421,7 @@ impl From> for v6::Settings { facet_search: v6::Setting::NotSet, prefix_search: v6::Setting::NotSet, chat: v6::Setting::NotSet, + vector_store: v6::Setting::NotSet, _kind: std::marker::PhantomData, } } diff --git a/crates/meilisearch-types/src/error.rs b/crates/meilisearch-types/src/error.rs index f651b2352..0f1782568 100644 --- a/crates/meilisearch-types/src/error.rs +++ b/crates/meilisearch-types/src/error.rs @@ -396,6 +396,7 @@ InvalidDocumentEditionContext , InvalidRequest , BAD_REQU InvalidDocumentEditionFunctionFilter , InvalidRequest , BAD_REQUEST ; EditDocumentsByFunctionError , InvalidRequest , BAD_REQUEST ; InvalidSettingsIndexChat , InvalidRequest , BAD_REQUEST ; +InvalidSettingsVectorStore , InvalidRequest , BAD_REQUEST ; // Export InvalidExportUrl , InvalidRequest , BAD_REQUEST ; InvalidExportApiKey , InvalidRequest , BAD_REQUEST ; diff --git a/crates/meilisearch-types/src/settings.rs b/crates/meilisearch-types/src/settings.rs index 9e107a5c3..e1d314b8f 100644 --- a/crates/meilisearch-types/src/settings.rs +++ b/crates/meilisearch-types/src/settings.rs @@ -14,6 +14,7 @@ use milli::proximity::ProximityPrecision; pub use milli::update::ChatSettings; use milli::update::Setting; use milli::vector::db::IndexEmbeddingConfig; +use milli::vector::VectorStoreBackend; use milli::{Criterion, CriterionError, FilterableAttributesRule, Index, DEFAULT_VALUES_PER_FACET}; use serde::{Deserialize, Serialize, Serializer}; use utoipa::ToSchema; @@ -320,6 +321,11 @@ pub struct Settings { #[schema(value_type = Option)] pub chat: Setting, + #[serde(default, skip_serializing_if = "Setting::is_not_set")] + #[deserr(default, error = DeserrJsonError)] + #[schema(value_type = Option)] + pub vector_store: Setting, + #[serde(skip)] #[deserr(skip)] pub _kind: PhantomData, @@ -386,6 +392,7 @@ impl Settings { facet_search: Setting::Reset, prefix_search: Setting::Reset, chat: Setting::Reset, + vector_store: Setting::Reset, _kind: PhantomData, } } @@ -413,6 +420,7 @@ impl Settings { facet_search, prefix_search, chat, + vector_store, _kind, } = self; @@ -437,6 +445,7 @@ impl Settings { localized_attributes: localized_attributes_rules, facet_search, prefix_search, + vector_store, chat, _kind: PhantomData, } @@ -489,6 +498,7 @@ impl Settings { facet_search: self.facet_search, prefix_search: self.prefix_search, chat: self.chat, + vector_store: self.vector_store, _kind: PhantomData, } } @@ -569,6 +579,7 @@ impl Settings { facet_search: other.facet_search.or(self.facet_search), prefix_search: other.prefix_search.or(self.prefix_search), chat: other.chat.clone().or(self.chat.clone()), + vector_store: other.vector_store.or(self.vector_store), _kind: PhantomData, } } @@ -608,6 +619,7 @@ pub fn apply_settings_to_builder( facet_search, prefix_search, chat, + vector_store, _kind, } = settings; @@ -825,6 +837,12 @@ pub fn apply_settings_to_builder( Setting::Reset => builder.reset_chat(), Setting::NotSet => (), } + + match vector_store { + Setting::Set(vector_store) => builder.set_vector_store(*vector_store), + Setting::Reset => builder.reset_vector_store(), + Setting::NotSet => (), + } } pub enum SecretPolicy { @@ -922,6 +940,9 @@ pub fn settings( (name, SettingEmbeddingSettings { inner: Setting::Set(config.into()) }) }) .collect(); + + let vector_store = index.get_vector_store(rtxn)?; + let embedders = Setting::Set(embedders); let search_cutoff_ms = index.search_cutoff(rtxn)?; let localized_attributes_rules = index.localized_attributes_rules(rtxn)?; @@ -968,6 +989,7 @@ pub fn settings( facet_search: Setting::Set(facet_search), prefix_search: Setting::Set(prefix_search.unwrap_or_default()), chat: Setting::Set(chat), + vector_store: Setting::Set(vector_store), _kind: PhantomData, }; @@ -1197,6 +1219,7 @@ pub(crate) mod test { facet_search: Setting::NotSet, prefix_search: Setting::NotSet, chat: Setting::NotSet, + vector_store: Setting::NotSet, _kind: PhantomData::, }; @@ -1229,6 +1252,7 @@ pub(crate) mod test { facet_search: Setting::NotSet, prefix_search: Setting::NotSet, chat: Setting::NotSet, + vector_store: Setting::NotSet, _kind: PhantomData::, }; diff --git a/crates/meilisearch/src/routes/indexes/settings.rs b/crates/meilisearch/src/routes/indexes/settings.rs index 10120ebff..336740162 100644 --- a/crates/meilisearch/src/routes/indexes/settings.rs +++ b/crates/meilisearch/src/routes/indexes/settings.rs @@ -520,6 +520,17 @@ make_setting_routes!( camelcase_attr: "chat", analytics: ChatAnalytics }, + { + route: "/vector-store", + update_verb: patch, + value_type: meilisearch_types::milli::vector::VectorStoreBackend, + err_type: meilisearch_types::deserr::DeserrJsonError< + meilisearch_types::error::deserr_codes::InvalidSettingsVectorStore, + >, + attr: vector_store, + camelcase_attr: "vectorStore", + analytics: VectorStoreAnalytics + }, ); #[utoipa::path( @@ -610,6 +621,7 @@ pub async fn update_all( facet_search: FacetSearchAnalytics::new(new_settings.facet_search.as_ref().set()), prefix_search: PrefixSearchAnalytics::new(new_settings.prefix_search.as_ref().set()), chat: ChatAnalytics::new(new_settings.chat.as_ref().set()), + vector_store: VectorStoreAnalytics::new(new_settings.vector_store.as_ref().set()), }, &req, ); @@ -665,10 +677,17 @@ pub async fn get_all( let index = index_scheduler.index(&index_uid)?; let rtxn = index.read_txn()?; let mut new_settings = settings(&index, &rtxn, SecretPolicy::HideSecrets)?; - if index_scheduler.features().check_chat_completions("showing index `chat` settings").is_err() { + + let features = index_scheduler.features(); + + if features.check_chat_completions("showing index `chat` settings").is_err() { new_settings.chat = Setting::NotSet; } + if features.check_vector_store_setting("showing index `vectorStore` settings").is_err() { + new_settings.vector_store = Setting::NotSet; + } + debug!(returns = ?new_settings, "Get all settings"); Ok(HttpResponse::Ok().json(new_settings)) } @@ -770,5 +789,9 @@ fn validate_settings( features.check_chat_completions("setting `chat` in the index settings")?; } + if let Setting::Set(_) = &settings.vector_store { + features.check_vector_store_setting("setting `vectorStore` in the index settings")?; + } + Ok(settings.validate()?) } diff --git a/crates/meilisearch/src/routes/indexes/settings_analytics.rs b/crates/meilisearch/src/routes/indexes/settings_analytics.rs index 1b8d0e244..cd573099f 100644 --- a/crates/meilisearch/src/routes/indexes/settings_analytics.rs +++ b/crates/meilisearch/src/routes/indexes/settings_analytics.rs @@ -8,6 +8,7 @@ use std::collections::{BTreeMap, BTreeSet, HashSet}; use meilisearch_types::facet_values_sort::FacetValuesSort; use meilisearch_types::locales::{Locale, LocalizedAttributesRuleView}; use meilisearch_types::milli::update::Setting; +use meilisearch_types::milli::vector::VectorStoreBackend; use meilisearch_types::milli::FilterableAttributesRule; use meilisearch_types::settings::{ ChatSettings, FacetingSettings, PaginationSettings, PrefixSearchSettings, @@ -40,6 +41,7 @@ pub struct SettingsAnalytics { pub facet_search: FacetSearchAnalytics, pub prefix_search: PrefixSearchAnalytics, pub chat: ChatAnalytics, + pub vector_store: VectorStoreAnalytics, } impl Aggregate for SettingsAnalytics { @@ -200,6 +202,10 @@ impl Aggregate for SettingsAnalytics { value: new.prefix_search.value.or(self.prefix_search.value), }, chat: ChatAnalytics { set: new.chat.set | self.chat.set }, + vector_store: VectorStoreAnalytics { + set: new.vector_store.set | self.vector_store.set, + value: new.vector_store.value.or(self.vector_store.value), + }, }) } @@ -693,3 +699,19 @@ impl ChatAnalytics { SettingsAnalytics { chat: self, ..Default::default() } } } + +#[derive(Serialize, Default)] +pub struct VectorStoreAnalytics { + pub set: bool, + pub value: Option, +} + +impl VectorStoreAnalytics { + pub fn new(settings: Option<&VectorStoreBackend>) -> Self { + Self { set: settings.is_some(), value: settings.copied() } + } + + pub fn into_settings(self) -> SettingsAnalytics { + SettingsAnalytics { vector_store: self, ..Default::default() } + } +}