From c1a132fa068e252d2554cd5acab489e9eea804b2 Mon Sep 17 00:00:00 2001 From: Louis Dureuil Date: Mon, 30 Jun 2025 13:54:04 +0200 Subject: [PATCH] `multimodal` experimental feature --- crates/index-scheduler/src/features.rs | 13 +++++++++++++ crates/meilisearch-types/src/features.rs | 1 + .../meilisearch/src/analytics/segment_analytics.rs | 3 +++ crates/meilisearch/src/routes/features.rs | 11 +++++++++++ crates/meilisearch/src/routes/indexes/settings.rs | 8 ++++++++ crates/meilisearch/src/search/mod.rs | 3 +++ 6 files changed, 39 insertions(+) diff --git a/crates/index-scheduler/src/features.rs b/crates/index-scheduler/src/features.rs index 78ffc0766..b52a659a6 100644 --- a/crates/index-scheduler/src/features.rs +++ b/crates/index-scheduler/src/features.rs @@ -144,6 +144,19 @@ impl RoFeatures { .into()) } } + + pub fn check_multimodal(&self, disabled_action: &'static str) -> Result<()> { + if self.runtime.multimodal { + Ok(()) + } else { + Err(FeatureNotEnabledError { + disabled_action, + feature: "multimodal", + issue_link: "https://github.com/orgs/meilisearch/discussions/846", + } + .into()) + } + } } impl FeatureData { diff --git a/crates/meilisearch-types/src/features.rs b/crates/meilisearch-types/src/features.rs index 9ec2d321f..3c78035e8 100644 --- a/crates/meilisearch-types/src/features.rs +++ b/crates/meilisearch-types/src/features.rs @@ -21,6 +21,7 @@ pub struct RuntimeTogglableFeatures { pub get_task_documents_route: bool, pub composite_embedders: bool, pub chat_completions: bool, + pub multimodal: bool, } #[derive(Default, Debug, Clone, Copy)] diff --git a/crates/meilisearch/src/analytics/segment_analytics.rs b/crates/meilisearch/src/analytics/segment_analytics.rs index 668a7fded..0abc5c817 100644 --- a/crates/meilisearch/src/analytics/segment_analytics.rs +++ b/crates/meilisearch/src/analytics/segment_analytics.rs @@ -197,6 +197,7 @@ struct Infos { experimental_max_number_of_batched_tasks: usize, experimental_limit_batched_tasks_total_size: u64, experimental_network: bool, + experimental_multimodal: bool, experimental_chat_completions: bool, experimental_get_task_documents_route: bool, experimental_composite_embedders: bool, @@ -303,6 +304,7 @@ impl Infos { get_task_documents_route, composite_embedders, chat_completions, + multimodal, } = features; // We're going to override every sensible information. @@ -322,6 +324,7 @@ impl Infos { experimental_reduce_indexing_memory_usage, experimental_network: network, experimental_chat_completions: chat_completions, + experimental_multimodal: multimodal, experimental_get_task_documents_route: get_task_documents_route, experimental_composite_embedders: composite_embedders, experimental_embedding_cache_entries, diff --git a/crates/meilisearch/src/routes/features.rs b/crates/meilisearch/src/routes/features.rs index 179b9cf68..1a1f89b2d 100644 --- a/crates/meilisearch/src/routes/features.rs +++ b/crates/meilisearch/src/routes/features.rs @@ -54,6 +54,7 @@ pub fn configure(cfg: &mut web::ServiceConfig) { get_task_documents_route: Some(false), composite_embedders: Some(false), chat_completions: Some(false), + multimodal: Some(false), })), (status = 401, description = "The authorization header is missing", body = ResponseError, content_type = "application/json", example = json!( { @@ -100,6 +101,8 @@ pub struct RuntimeTogglableFeatures { pub composite_embedders: Option, #[deserr(default)] pub chat_completions: Option, + #[deserr(default)] + pub multimodal: Option, } impl From for RuntimeTogglableFeatures { @@ -113,6 +116,7 @@ impl From for RuntimeTogg get_task_documents_route, composite_embedders, chat_completions, + multimodal, } = value; Self { @@ -124,6 +128,7 @@ impl From for RuntimeTogg get_task_documents_route: Some(get_task_documents_route), composite_embedders: Some(composite_embedders), chat_completions: Some(chat_completions), + multimodal: Some(multimodal), } } } @@ -138,6 +143,7 @@ pub struct PatchExperimentalFeatureAnalytics { get_task_documents_route: bool, composite_embedders: bool, chat_completions: bool, + multimodal: bool, } impl Aggregate for PatchExperimentalFeatureAnalytics { @@ -155,6 +161,7 @@ impl Aggregate for PatchExperimentalFeatureAnalytics { get_task_documents_route: new.get_task_documents_route, composite_embedders: new.composite_embedders, chat_completions: new.chat_completions, + multimodal: new.multimodal, }) } @@ -181,6 +188,7 @@ impl Aggregate for PatchExperimentalFeatureAnalytics { get_task_documents_route: Some(false), composite_embedders: Some(false), chat_completions: Some(false), + multimodal: Some(false), })), (status = 401, description = "The authorization header is missing", body = ResponseError, content_type = "application/json", example = json!( { @@ -223,6 +231,7 @@ async fn patch_features( .composite_embedders .unwrap_or(old_features.composite_embedders), chat_completions: new_features.0.chat_completions.unwrap_or(old_features.chat_completions), + multimodal: new_features.0.multimodal.unwrap_or(old_features.multimodal), }; // explicitly destructure for analytics rather than using the `Serialize` implementation, because @@ -237,6 +246,7 @@ async fn patch_features( get_task_documents_route, composite_embedders, chat_completions, + multimodal, } = new_features; analytics.publish( @@ -249,6 +259,7 @@ async fn patch_features( get_task_documents_route, composite_embedders, chat_completions, + multimodal, }, &req, ); diff --git a/crates/meilisearch/src/routes/indexes/settings.rs b/crates/meilisearch/src/routes/indexes/settings.rs index a4b7a5219..308977a6e 100644 --- a/crates/meilisearch/src/routes/indexes/settings.rs +++ b/crates/meilisearch/src/routes/indexes/settings.rs @@ -755,6 +755,14 @@ fn validate_settings( if matches!(embedder.indexing_embedder, Setting::Set(_)) { features.check_composite_embedders("setting `indexingEmbedder`")?; } + + if matches!(embedder.indexing_fragments, Setting::Set(_)) { + features.check_multimodal("setting `indexingFragments`")?; + } + + if matches!(embedder.search_fragments, Setting::Set(_)) { + features.check_multimodal("setting `searchFragments`")?; + } } } diff --git a/crates/meilisearch/src/search/mod.rs b/crates/meilisearch/src/search/mod.rs index 6d8639504..1c987a70c 100644 --- a/crates/meilisearch/src/search/mod.rs +++ b/crates/meilisearch/src/search/mod.rs @@ -972,6 +972,9 @@ pub fn prepare_search<'t>( time_budget: TimeBudget, features: RoFeatures, ) -> Result<(milli::Search<'t>, bool, usize, usize), ResponseError> { + if query.media.is_some() { + features.check_multimodal("passing `media` in a search query")?; + } let mut search = index.search(rtxn); search.time_budget(time_budget); if let Some(ranking_score_threshold) = query.ranking_score_threshold {