From 705b195941b39101031b6109804272d9315268f7 Mon Sep 17 00:00:00 2001 From: Mubelotix Date: Mon, 28 Jul 2025 12:37:44 +0200 Subject: [PATCH] Make updating settings override previous value --- crates/meilisearch/tests/common/index.rs | 2 +- crates/meilisearch/tests/settings/chat.rs | 4 +- crates/milli/src/update/chat.rs | 138 ++++++++++++++-------- crates/milli/src/update/settings.rs | 95 +-------------- 4 files changed, 93 insertions(+), 146 deletions(-) diff --git a/crates/meilisearch/tests/common/index.rs b/crates/meilisearch/tests/common/index.rs index 5cbca87da..bddc94d84 100644 --- a/crates/meilisearch/tests/common/index.rs +++ b/crates/meilisearch/tests/common/index.rs @@ -180,7 +180,7 @@ impl<'a> Index<'a, Owned> { ) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/chat", urlencode(self.uid.as_ref())); - self.service.patch_encoded(url, settings, self.encoder).await + self.service.put_encoded(url, settings, self.encoder).await } pub async fn update_settings_displayed_attributes( diff --git a/crates/meilisearch/tests/settings/chat.rs b/crates/meilisearch/tests/settings/chat.rs index 53ad20b63..261443228 100644 --- a/crates/meilisearch/tests/settings/chat.rs +++ b/crates/meilisearch/tests/settings/chat.rs @@ -55,9 +55,7 @@ async fn set_reset_chat_issue_5772() { "documentTemplate": "{% for field in fields %}{% if field.is_searchable and field.value != nil %}{{ field.name }}: {{ field.value }}\n{% endif %}{% endfor %}", "documentTemplateMaxBytes": 400, "searchParameters": { - "limit": 16, - "sort": [], - "attributesToSearchOn": [] + "limit": 16 } } "#); diff --git a/crates/milli/src/update/chat.rs b/crates/milli/src/update/chat.rs index 2f364894d..9955b5422 100644 --- a/crates/milli/src/update/chat.rs +++ b/crates/milli/src/update/chat.rs @@ -1,44 +1,38 @@ use std::error::Error; use std::fmt; +use std::num::NonZeroUsize; use deserr::errors::JsonError; use deserr::Deserr; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; -use crate::index::{self, ChatConfig, MatchingStrategy, RankingScoreThreshold, SearchParameters}; -use crate::prompt::{default_max_bytes, PromptData}; -use crate::update::Setting; +use crate::index::{ChatConfig, MatchingStrategy, RankingScoreThreshold, SearchParameters}; +use crate::prompt::PromptData; #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Deserr, ToSchema)] #[serde(deny_unknown_fields, rename_all = "camelCase")] #[deserr(error = JsonError, deny_unknown_fields, rename_all = camelCase)] pub struct ChatSettings { - #[serde(default, skip_serializing_if = "Setting::is_not_set")] - #[deserr(default)] - #[schema(value_type = Option)] - pub description: Setting, + pub description: String, /// A liquid template used to render documents to a text that can be embedded. /// /// Meillisearch interpolates the template for each document and sends the resulting text to the embedder. /// The embedder then generates document vectors based on this text. - #[serde(default, skip_serializing_if = "Setting::is_not_set")] - #[deserr(default)] - #[schema(value_type = Option)] - pub document_template: Setting, + pub document_template: String, /// Rendered texts are truncated to this size. Defaults to 400. - #[serde(default, skip_serializing_if = "Setting::is_not_set")] + #[serde(default, skip_serializing_if = "Option::is_none")] #[deserr(default)] #[schema(value_type = Option)] - pub document_template_max_bytes: Setting, + pub document_template_max_bytes: Option, /// The search parameters to use for the LLM. - #[serde(default, skip_serializing_if = "Setting::is_not_set")] + #[serde(default, skip_serializing_if = "Option::is_none")] #[deserr(default)] #[schema(value_type = Option)] - pub search_parameters: Setting, + pub search_parameters: Option, } impl From for ChatSettings { @@ -49,12 +43,10 @@ impl From for ChatSettings { search_parameters, } = config; ChatSettings { - description: Setting::Set(description), - document_template: Setting::Set(template), - document_template_max_bytes: Setting::Set( - max_bytes.unwrap_or(default_max_bytes()).get(), - ), - search_parameters: Setting::Set({ + description, + document_template: template, + document_template_max_bytes: max_bytes, + search_parameters: { let SearchParameters { hybrid, limit, @@ -65,20 +57,68 @@ impl From for ChatSettings { ranking_score_threshold, } = search_parameters; - let hybrid = hybrid.map(|index::HybridQuery { semantic_ratio, embedder }| { - HybridQuery { semantic_ratio: SemanticRatio(semantic_ratio), embedder } - }); - - ChatSearchParams { - hybrid: Setting::some_or_not_set(hybrid), - limit: Setting::some_or_not_set(limit), - sort: Setting::some_or_not_set(sort), - distinct: Setting::some_or_not_set(distinct), - matching_strategy: Setting::some_or_not_set(matching_strategy), - attributes_to_search_on: Setting::some_or_not_set(attributes_to_search_on), - ranking_score_threshold: Setting::some_or_not_set(ranking_score_threshold), + if hybrid.is_none() && + limit.is_none() && + sort.is_none() && + distinct.is_none() && + matching_strategy.is_none() && + attributes_to_search_on.is_none() && + ranking_score_threshold.is_none() + { + None + } else { + Some(ChatSearchParams { + hybrid: hybrid.map(|h| HybridQuery { + semantic_ratio: SemanticRatio(h.semantic_ratio), + embedder: h.embedder, + }), + limit, + sort, + distinct, + matching_strategy, + attributes_to_search_on, + ranking_score_threshold, + }) } - }), + }, + } + } +} + +impl From for ChatConfig { + fn from(settings: ChatSettings) -> Self { + let ChatSettings { + description, + document_template, + document_template_max_bytes, + search_parameters, + } = settings; + + let prompt = PromptData { + template: document_template, + max_bytes: document_template_max_bytes, + }; + + let search_parameters = match search_parameters { + Some(params) => SearchParameters { + hybrid: params.hybrid.map(|h| crate::index::HybridQuery { + semantic_ratio: h.semantic_ratio.0, + embedder: h.embedder, + }), + limit: params.limit, + sort: params.sort, + distinct: params.distinct, + matching_strategy: params.matching_strategy, + attributes_to_search_on: params.attributes_to_search_on, + ranking_score_threshold: params.ranking_score_threshold, + }, + None => SearchParameters::default(), + }; + + ChatConfig { + description, + prompt, + search_parameters, } } } @@ -87,40 +127,40 @@ impl From for ChatSettings { #[serde(deny_unknown_fields, rename_all = "camelCase")] #[deserr(error = JsonError, deny_unknown_fields, rename_all = camelCase)] pub struct ChatSearchParams { - #[serde(default, skip_serializing_if = "Setting::is_not_set")] + #[serde(default, skip_serializing_if = "Option::is_none")] #[deserr(default)] #[schema(value_type = Option)] - pub hybrid: Setting, + pub hybrid: Option, - #[serde(default, skip_serializing_if = "Setting::is_not_set")] - #[deserr(default = Setting::Set(20))] + #[serde(default, skip_serializing_if = "Option::is_none")] + #[deserr(default)] #[schema(value_type = Option)] - pub limit: Setting, + pub limit: Option, - #[serde(default, skip_serializing_if = "Setting::is_not_set")] + #[serde(default, skip_serializing_if = "Option::is_none")] #[deserr(default)] #[schema(value_type = Option>)] - pub sort: Setting>, + pub sort: Option>, - #[serde(default, skip_serializing_if = "Setting::is_not_set")] + #[serde(default, skip_serializing_if = "Option::is_none")] #[deserr(default)] #[schema(value_type = Option)] - pub distinct: Setting, + pub distinct: Option, - #[serde(default, skip_serializing_if = "Setting::is_not_set")] + #[serde(default, skip_serializing_if = "Option::is_none")] #[deserr(default)] #[schema(value_type = Option)] - pub matching_strategy: Setting, + pub matching_strategy: Option, - #[serde(default, skip_serializing_if = "Setting::is_not_set")] + #[serde(default, skip_serializing_if = "Option::is_none")] #[deserr(default)] #[schema(value_type = Option>)] - pub attributes_to_search_on: Setting>, + pub attributes_to_search_on: Option>, - #[serde(default, skip_serializing_if = "Setting::is_not_set")] + #[serde(default, skip_serializing_if = "Option::is_none")] #[deserr(default)] #[schema(value_type = Option)] - pub ranking_score_threshold: Setting, + pub ranking_score_threshold: Option, } #[derive(Debug, Clone, Default, Deserr, ToSchema, PartialEq, Serialize, Deserialize)] diff --git a/crates/milli/src/update/settings.rs b/crates/milli/src/update/settings.rs index 911f51865..50e83555f 100644 --- a/crates/milli/src/update/settings.rs +++ b/crates/milli/src/update/settings.rs @@ -1304,101 +1304,10 @@ impl<'a, 't, 'i> Settings<'a, 't, 'i> { fn update_chat_config(&mut self) -> Result { match &mut self.chat { - Setting::Set(ChatSettings { - description: new_description, - document_template: new_document_template, - document_template_max_bytes: new_document_template_max_bytes, - search_parameters: new_search_parameters, - }) => { - let ChatConfig { description, prompt, search_parameters } = - self.index.chat_config(self.wtxn)?; - - let description = match new_description { - Setting::Set(new) => new.clone(), - Setting::Reset => Default::default(), - Setting::NotSet => description, - }; - - let prompt = PromptData { - template: match new_document_template { - Setting::Set(new) => new.clone(), - Setting::Reset => default_template_text().to_string(), - Setting::NotSet => prompt.template.clone(), - }, - max_bytes: match new_document_template_max_bytes { - Setting::Set(m) => Some( - NonZeroUsize::new(*m) - .ok_or(InvalidChatSettingsDocumentTemplateMaxBytes)?, - ), - Setting::Reset => Some(default_max_bytes()), - Setting::NotSet => prompt.max_bytes, - }, - }; - - let search_parameters = match new_search_parameters { - Setting::Set(sp) => { - let ChatSearchParams { - hybrid, - limit, - sort, - distinct, - matching_strategy, - attributes_to_search_on, - ranking_score_threshold, - } = sp; - - SearchParameters { - hybrid: match hybrid { - Setting::Set(hybrid) => Some(crate::index::HybridQuery { - semantic_ratio: *hybrid.semantic_ratio, - embedder: hybrid.embedder.clone(), - }), - Setting::Reset => None, - Setting::NotSet => search_parameters.hybrid.clone(), - }, - limit: match limit { - Setting::Set(limit) => Some(*limit), - Setting::Reset => None, - Setting::NotSet => search_parameters.limit, - }, - sort: match sort { - Setting::Set(sort) => Some(sort.clone()), - Setting::Reset => None, - Setting::NotSet => search_parameters.sort.clone(), - }, - distinct: match distinct { - Setting::Set(distinct) => Some(distinct.clone()), - Setting::Reset => None, - Setting::NotSet => search_parameters.distinct.clone(), - }, - matching_strategy: match matching_strategy { - Setting::Set(matching_strategy) => Some(*matching_strategy), - Setting::Reset => None, - Setting::NotSet => search_parameters.matching_strategy, - }, - attributes_to_search_on: match attributes_to_search_on { - Setting::Set(attributes_to_search_on) => { - Some(attributes_to_search_on.clone()) - } - Setting::Reset => None, - Setting::NotSet => { - search_parameters.attributes_to_search_on.clone() - } - }, - ranking_score_threshold: match ranking_score_threshold { - Setting::Set(rst) => Some(*rst), - Setting::Reset => None, - Setting::NotSet => search_parameters.ranking_score_threshold, - }, - } - } - Setting::Reset => Default::default(), - Setting::NotSet => search_parameters, - }; - + Setting::Set(settings) => { self.index.put_chat_config( self.wtxn, - &ChatConfig { description, prompt, search_parameters }, + &settings.clone().into(), )?; Ok(true)