From eb7230c2f67b1ae792f104098a31cebed51e6371 Mon Sep 17 00:00:00 2001 From: Martin Tzvetanov Grigorov Date: Mon, 23 Jun 2025 11:11:19 +0300 Subject: [PATCH 1/4] Fixes #5669 - Enable `chatCompletions` experimental feature via CLI and ENV https://www.meilisearch.com/docs/reference/api/chats#chat-completions-workspace-object Currently the new `chatCompletions` feature could be enabled only via ``` curl \ -X PATCH 'MEILISEARCH_URL/experimental-features/' \ -H 'Content-Type: application/json' \ --data-binary '{ "chatCompletions": true }' ``` Now it will be possible to use 1) command line argument: `--experimental_chat_completions` 2) or environment variable: `export MEILI_EXPERIMENTAL_CHAT_COMPLETIONS=true` Signed-off-by: Martin Tzvetanov Grigorov --- crates/index-scheduler/src/lib.rs | 29 ++++++++++++++++--- .../src/analytics/segment_analytics.rs | 1 + crates/meilisearch/src/lib.rs | 1 + crates/meilisearch/src/option.rs | 21 +++++++++----- 4 files changed, 41 insertions(+), 11 deletions(-) diff --git a/crates/index-scheduler/src/lib.rs b/crates/index-scheduler/src/lib.rs index 505ce23f8..eed847662 100644 --- a/crates/index-scheduler/src/lib.rs +++ b/crates/index-scheduler/src/lib.rs @@ -45,6 +45,9 @@ use std::path::{Path, PathBuf}; use std::sync::{Arc, RwLock}; use std::time::Duration; +use crate::features::FeatureData; +use crate::index_mapper::IndexMapper; +use crate::utils::clamp_to_page_size; use dump::Dump; pub use error::Error; pub use features::RoFeatures; @@ -71,9 +74,6 @@ use scheduler::Scheduler; use time::OffsetDateTime; use versioning::Versioning; -use crate::index_mapper::IndexMapper; -use crate::utils::clamp_to_page_size; - pub(crate) type BEI128 = I128; const TASK_SCHEDULER_SIZE_THRESHOLD_PERCENT_INT: u64 = 40; @@ -136,6 +136,8 @@ pub struct IndexSchedulerOptions { pub embedding_cache_cap: usize, /// Snapshot compaction status. pub experimental_no_snapshot_compaction: bool, + /// Whether the chat completions are enabled or not. + pub experimental_chat_completions: bool, } /// Structure which holds meilisearch's indexes and schedules the tasks @@ -273,7 +275,7 @@ impl IndexScheduler { .open(&options.tasks_path) }?; - // We **must** starts by upgrading the version because it'll also upgrade the required database before we can open them + // We **must** start by upgrading the version because it'll also upgrade the required database before we can open them let version = versioning::Versioning::new(&env, from_db_version)?; let mut wtxn = env.write_txn()?; @@ -281,8 +283,11 @@ impl IndexScheduler { let queue = Queue::new(&env, &mut wtxn, &options)?; let index_mapper = IndexMapper::new(&env, &mut wtxn, &options, budget)?; let chat_settings = env.create_database(&mut wtxn, Some(CHAT_SETTINGS_DB_NAME))?; + wtxn.commit()?; + configure_experimental_features(&env, &features, &options)?; + // allow unreachable_code to get rids of the warning in the case of a test build. let this = Self { processing_tasks: Arc::new(RwLock::new(ProcessingTasks::new())), @@ -922,6 +927,22 @@ impl IndexScheduler { } } +fn configure_experimental_features( + env: &Env, + features: &FeatureData, + options: &IndexSchedulerOptions, +) -> Result<()> { + let current_features = features.features().runtime_features(); + + let new_features = RuntimeTogglableFeatures { + chat_completions: options.experimental_chat_completions, + ..current_features + }; + + let wtxn = env.write_txn().map_err(Error::HeedTransaction)?; + features.put_runtime_features(wtxn, new_features) +} + /// The outcome of calling the [`IndexScheduler::tick`] function. pub enum TickOutcome { /// The scheduler should immediately attempt another `tick`. diff --git a/crates/meilisearch/src/analytics/segment_analytics.rs b/crates/meilisearch/src/analytics/segment_analytics.rs index c7e0634f4..6ab4e88b8 100644 --- a/crates/meilisearch/src/analytics/segment_analytics.rs +++ b/crates/meilisearch/src/analytics/segment_analytics.rs @@ -279,6 +279,7 @@ impl Infos { indexer_options, config_file_path, no_analytics: _, + experimental_chat_completions: _, } = options; let schedule_snapshot = match schedule_snapshot { diff --git a/crates/meilisearch/src/lib.rs b/crates/meilisearch/src/lib.rs index 1e0c205d0..b376a967e 100644 --- a/crates/meilisearch/src/lib.rs +++ b/crates/meilisearch/src/lib.rs @@ -237,6 +237,7 @@ pub fn setup_meilisearch(opt: &Opt) -> anyhow::Result<(Arc, Arc< auto_upgrade: opt.experimental_dumpless_upgrade, embedding_cache_cap: opt.experimental_embedding_cache_entries, experimental_no_snapshot_compaction: opt.experimental_no_snapshot_compaction, + experimental_chat_completions: opt.experimental_chat_completions, }; let binary_version = (VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); diff --git a/crates/meilisearch/src/option.rs b/crates/meilisearch/src/option.rs index 35ce71cf4..36704b712 100644 --- a/crates/meilisearch/src/option.rs +++ b/crates/meilisearch/src/option.rs @@ -66,6 +66,7 @@ const MEILI_EXPERIMENTAL_LIMIT_BATCHED_TASKS_TOTAL_SIZE: &str = const MEILI_EXPERIMENTAL_EMBEDDING_CACHE_ENTRIES: &str = "MEILI_EXPERIMENTAL_EMBEDDING_CACHE_ENTRIES"; const MEILI_EXPERIMENTAL_NO_SNAPSHOT_COMPACTION: &str = "MEILI_EXPERIMENTAL_NO_SNAPSHOT_COMPACTION"; +const MEILI_EXPERIMENTAL_CHAT_COMPLETIONS: &str = "MEILI_EXPERIMENTAL_CHAT_COMPLETIONS"; const DEFAULT_CONFIG_FILE_PATH: &str = "./config.toml"; const DEFAULT_DB_PATH: &str = "./data.ms"; const DEFAULT_HTTP_ADDR: &str = "localhost:7700"; @@ -473,6 +474,11 @@ pub struct Opt { /// Format must be TOML. #[clap(long)] pub config_file_path: Option, + + /// Enables the chat completions functionality. + #[serde(default)] + #[clap(long, env = MEILI_EXPERIMENTAL_CHAT_COMPLETIONS, default_value_t = false)] + pub experimental_chat_completions: bool, } impl Opt { @@ -498,25 +504,21 @@ impl Opt { Ok(config) => { // If the file is successfully read, we deserialize it with `toml`. let opt_from_config = toml::from_str::(&config)?; - // Return an error if config file contains 'config_file_path' + // Return an error if the config file contains 'config_file_path' // Using that key in the config file doesn't make sense bc it creates a logical loop (config file referencing itself) if opt_from_config.config_file_path.is_some() { anyhow::bail!("`config_file_path` is not supported in the configuration file") } // We inject the values from the toml in the corresponding env vars if needs be. Doing so, we respect the priority toml < env vars < cli args. opt_from_config.export_to_env(); - // Once injected we parse the cli args once again to take the new env vars into scope. + // Once injected, we parse the cli args once again to take the new env vars into scope. opts = Opt::parse(); config_read_from = Some(config_file_path); } Err(e) => { if let Some(path) = user_specified_config_file_path { // If we have an error while reading the file defined by the user. - anyhow::bail!( - "unable to open or read the {:?} configuration file: {}.", - path, - e, - ) + anyhow::bail!("unable to open or read the {path:?} configuration file: {e}.") } } } @@ -570,6 +572,7 @@ impl Opt { experimental_limit_batched_tasks_total_size, experimental_embedding_cache_entries, experimental_no_snapshot_compaction, + experimental_chat_completions, } = self; export_to_env_if_not_present(MEILI_DB_PATH, db_path); export_to_env_if_not_present(MEILI_HTTP_ADDR, http_addr); @@ -670,6 +673,10 @@ impl Opt { MEILI_EXPERIMENTAL_NO_SNAPSHOT_COMPACTION, experimental_no_snapshot_compaction.to_string(), ); + export_to_env_if_not_present( + MEILI_EXPERIMENTAL_CHAT_COMPLETIONS, + experimental_chat_completions.to_string(), + ); indexer_options.export_to_env(); } From 0ebdda15c607a85228f46a5c182f217d8b947c2e Mon Sep 17 00:00:00 2001 From: Martin Tzvetanov Grigorov Date: Mon, 23 Jun 2025 11:31:52 +0300 Subject: [PATCH 2/4] Add `experimental_chat_completions` to the test_utils Signed-off-by: Martin Tzvetanov Grigorov --- crates/index-scheduler/src/test_utils.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/index-scheduler/src/test_utils.rs b/crates/index-scheduler/src/test_utils.rs index 5f206b55c..2382db041 100644 --- a/crates/index-scheduler/src/test_utils.rs +++ b/crates/index-scheduler/src/test_utils.rs @@ -114,6 +114,7 @@ impl IndexScheduler { auto_upgrade: true, // Don't cost much and will ensure the happy path works embedding_cache_cap: 10, experimental_no_snapshot_compaction: false, + experimental_chat_completions: false, }; let version = configuration(&mut options).unwrap_or({ (versioning::VERSION_MAJOR, versioning::VERSION_MINOR, versioning::VERSION_PATCH) From da5f0de153217757be7044557c6f8f41f668c5e9 Mon Sep 17 00:00:00 2001 From: Martin Tzvetanov Grigorov Date: Mon, 23 Jun 2025 11:32:14 +0300 Subject: [PATCH 3/4] Document the new cli argument/option in the config file Signed-off-by: Martin Tzvetanov Grigorov --- config.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config.toml b/config.toml index bbd70a63f..fefe9d3b7 100644 --- a/config.toml +++ b/config.toml @@ -132,3 +132,6 @@ experimental_reduce_indexing_memory_usage = false # Experimentally reduces the maximum number of tasks that will be processed at once, see: # experimental_max_number_of_batched_tasks = 100 + +# Experimental chat completions feature, see: +experimental_chat_completions = false From fe7245af20f74b7a797cf200a553f73691b8fdff Mon Sep 17 00:00:00 2001 From: Martin Tzvetanov Grigorov Date: Wed, 25 Jun 2025 09:44:49 +0300 Subject: [PATCH 4/4] Add chat_completions to InstanceTogglableFeatures Signed-off-by: Martin Tzvetanov Grigorov --- crates/index-scheduler/src/features.rs | 4 +++- crates/index-scheduler/src/lib.rs | 22 ---------------------- crates/index-scheduler/src/test_utils.rs | 1 - crates/meilisearch-types/src/features.rs | 1 + crates/meilisearch/src/lib.rs | 1 - crates/meilisearch/src/option.rs | 1 + 6 files changed, 5 insertions(+), 25 deletions(-) diff --git a/crates/index-scheduler/src/features.rs b/crates/index-scheduler/src/features.rs index 78ffc0766..89495264c 100644 --- a/crates/index-scheduler/src/features.rs +++ b/crates/index-scheduler/src/features.rs @@ -161,11 +161,13 @@ impl FeatureData { let persisted_features: RuntimeTogglableFeatures = runtime_features_db.get(wtxn, db_keys::EXPERIMENTAL_FEATURES)?.unwrap_or_default(); - let InstanceTogglableFeatures { metrics, logs_route, contains_filter } = instance_features; + let InstanceTogglableFeatures { metrics, logs_route, contains_filter, chat_completions } = + instance_features; let runtime = Arc::new(RwLock::new(RuntimeTogglableFeatures { metrics: metrics || persisted_features.metrics, logs_route: logs_route || persisted_features.logs_route, contains_filter: contains_filter || persisted_features.contains_filter, + chat_completions: chat_completions || persisted_features.chat_completions, ..persisted_features })); diff --git a/crates/index-scheduler/src/lib.rs b/crates/index-scheduler/src/lib.rs index eed847662..e9d0b2374 100644 --- a/crates/index-scheduler/src/lib.rs +++ b/crates/index-scheduler/src/lib.rs @@ -45,7 +45,6 @@ use std::path::{Path, PathBuf}; use std::sync::{Arc, RwLock}; use std::time::Duration; -use crate::features::FeatureData; use crate::index_mapper::IndexMapper; use crate::utils::clamp_to_page_size; use dump::Dump; @@ -136,8 +135,6 @@ pub struct IndexSchedulerOptions { pub embedding_cache_cap: usize, /// Snapshot compaction status. pub experimental_no_snapshot_compaction: bool, - /// Whether the chat completions are enabled or not. - pub experimental_chat_completions: bool, } /// Structure which holds meilisearch's indexes and schedules the tasks @@ -283,11 +280,8 @@ impl IndexScheduler { let queue = Queue::new(&env, &mut wtxn, &options)?; let index_mapper = IndexMapper::new(&env, &mut wtxn, &options, budget)?; let chat_settings = env.create_database(&mut wtxn, Some(CHAT_SETTINGS_DB_NAME))?; - wtxn.commit()?; - configure_experimental_features(&env, &features, &options)?; - // allow unreachable_code to get rids of the warning in the case of a test build. let this = Self { processing_tasks: Arc::new(RwLock::new(ProcessingTasks::new())), @@ -927,22 +921,6 @@ impl IndexScheduler { } } -fn configure_experimental_features( - env: &Env, - features: &FeatureData, - options: &IndexSchedulerOptions, -) -> Result<()> { - let current_features = features.features().runtime_features(); - - let new_features = RuntimeTogglableFeatures { - chat_completions: options.experimental_chat_completions, - ..current_features - }; - - let wtxn = env.write_txn().map_err(Error::HeedTransaction)?; - features.put_runtime_features(wtxn, new_features) -} - /// The outcome of calling the [`IndexScheduler::tick`] function. pub enum TickOutcome { /// The scheduler should immediately attempt another `tick`. diff --git a/crates/index-scheduler/src/test_utils.rs b/crates/index-scheduler/src/test_utils.rs index 2382db041..5f206b55c 100644 --- a/crates/index-scheduler/src/test_utils.rs +++ b/crates/index-scheduler/src/test_utils.rs @@ -114,7 +114,6 @@ impl IndexScheduler { auto_upgrade: true, // Don't cost much and will ensure the happy path works embedding_cache_cap: 10, experimental_no_snapshot_compaction: false, - experimental_chat_completions: false, }; let version = configuration(&mut options).unwrap_or({ (versioning::VERSION_MAJOR, versioning::VERSION_MINOR, versioning::VERSION_PATCH) diff --git a/crates/meilisearch-types/src/features.rs b/crates/meilisearch-types/src/features.rs index 83054e784..21ee0e53c 100644 --- a/crates/meilisearch-types/src/features.rs +++ b/crates/meilisearch-types/src/features.rs @@ -28,6 +28,7 @@ pub struct InstanceTogglableFeatures { pub metrics: bool, pub logs_route: bool, pub contains_filter: bool, + pub chat_completions: bool, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] diff --git a/crates/meilisearch/src/lib.rs b/crates/meilisearch/src/lib.rs index b376a967e..1e0c205d0 100644 --- a/crates/meilisearch/src/lib.rs +++ b/crates/meilisearch/src/lib.rs @@ -237,7 +237,6 @@ pub fn setup_meilisearch(opt: &Opt) -> anyhow::Result<(Arc, Arc< auto_upgrade: opt.experimental_dumpless_upgrade, embedding_cache_cap: opt.experimental_embedding_cache_entries, experimental_no_snapshot_compaction: opt.experimental_no_snapshot_compaction, - experimental_chat_completions: opt.experimental_chat_completions, }; let binary_version = (VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); diff --git a/crates/meilisearch/src/option.rs b/crates/meilisearch/src/option.rs index 36704b712..7aa34e801 100644 --- a/crates/meilisearch/src/option.rs +++ b/crates/meilisearch/src/option.rs @@ -729,6 +729,7 @@ impl Opt { metrics: self.experimental_enable_metrics, logs_route: self.experimental_enable_logs_route, contains_filter: self.experimental_contains_filter, + chat_completions: self.experimental_chat_completions, } } }