Add support for ugly names

This commit is contained in:
Mubelotix
2025-08-01 10:22:24 +02:00
parent dae4fa874c
commit 7f394d59cd
6 changed files with 108 additions and 81 deletions

View File

@ -18,7 +18,7 @@ use nom::sequence::{terminated, tuple};
use Condition::*; use Condition::*;
use crate::error::IResultExt; use crate::error::IResultExt;
use crate::value::parse_vector_value; use crate::value::parse_dotted_value_part;
use crate::ErrorKind; use crate::ErrorKind;
use crate::VectorFilter; use crate::VectorFilter;
use crate::{parse_value, FilterCondition, IResult, Span, Token}; use crate::{parse_value, FilterCondition, IResult, Span, Token};
@ -136,13 +136,13 @@ fn parse_vectors(input: Span) -> IResult<(Token, Option<Token>, VectorFilter<'_>
// We could use nom's `cut` but it's better to be explicit about the errors // We could use nom's `cut` but it's better to be explicit about the errors
let (input, embedder_name) = let (input, embedder_name) =
parse_vector_value(input).map_cut(ErrorKind::VectorFilterInvalidEmbedder)?; parse_dotted_value_part(input).map_cut(ErrorKind::VectorFilterInvalidEmbedder)?;
let (input, filter) = alt(( let (input, filter) = alt((
map( map(
preceded(tag(".fragments"), |input| { preceded(tag(".fragments"), |input| {
let (input, _) = tag(".")(input).map_cut(ErrorKind::VectorFilterMissingFragment)?; let (input, _) = tag(".")(input).map_cut(ErrorKind::VectorFilterMissingFragment)?;
parse_vector_value(input).map_cut(ErrorKind::VectorFilterInvalidFragment) parse_dotted_value_part(input).map_cut(ErrorKind::VectorFilterInvalidFragment)
}), }),
VectorFilter::Fragment, VectorFilter::Fragment,
), ),

View File

@ -61,7 +61,9 @@ use nom::multi::{many0, separated_list1};
use nom::number::complete::recognize_float; use nom::number::complete::recognize_float;
use nom::sequence::{delimited, preceded, terminated, tuple}; use nom::sequence::{delimited, preceded, terminated, tuple};
use nom::Finish; use nom::Finish;
pub use nom::Slice;
use nom_locate::LocatedSpan; use nom_locate::LocatedSpan;
pub use value::parse_dotted_value_part;
pub(crate) use value::parse_value; pub(crate) use value::parse_value;
use value::word_exact; use value::word_exact;

View File

@ -80,8 +80,8 @@ pub fn word_exact<'a, 'b: 'a>(tag: &'b str) -> impl Fn(Span<'a>) -> IResult<'a,
} }
} }
/// vector_value = ( non_dot_word | singleQuoted | doubleQuoted) /// dotted_value_part = ( non_dot_word | singleQuoted | doubleQuoted)
pub fn parse_vector_value(input: Span) -> IResult<Token> { pub fn parse_dotted_value_part(input: Span) -> IResult<Token> {
pub fn non_dot_word(input: Span) -> IResult<Token> { pub fn non_dot_word(input: Span) -> IResult<Token> {
let (input, word) = take_while1(|c| is_value_component(c) && c != '.')(input)?; let (input, word) = take_while1(|c| is_value_component(c) && c != '.')(input)?;
Ok((input, word.into())) Ok((input, word.into()))

View File

@ -20,6 +20,7 @@ use meilisearch_types::milli::prompt::{get_document, get_inline_document_fields}
use meilisearch_types::milli::vector::db::IndexEmbeddingConfig; use meilisearch_types::milli::vector::db::IndexEmbeddingConfig;
use meilisearch_types::milli::vector::json_template::{self, JsonTemplate}; use meilisearch_types::milli::vector::json_template::{self, JsonTemplate};
use meilisearch_types::milli::vector::EmbedderOptions; use meilisearch_types::milli::vector::EmbedderOptions;
use meilisearch_types::milli::{Span, Token};
use meilisearch_types::{heed, milli, Index}; use meilisearch_types::{heed, milli, Index};
use serde::Serialize; use serde::Serialize;
use serde_json::Value; use serde_json::Value;
@ -133,47 +134,49 @@ impl FragmentKind {
} }
} }
enum RenderError { enum RenderError<'a> {
MultipleTemplates, MultipleTemplates,
MissingTemplate, MissingTemplate,
EmptyTemplateId, EmptyTemplateId,
UnknownTemplateRoot(String), UnknownTemplateRoot(Token<'a>),
MissingEmbedderName { MissingEmbedderName {
available: Vec<String>, available: Vec<String>,
}, },
EmbedderDoesNotExist { EmbedderDoesNotExist {
embedder: String, embedder: Token<'a>,
available: Vec<String>, available: Vec<String>,
}, },
EmbedderUsesFragments { EmbedderUsesFragments {
embedder: String, embedder: Token<'a>,
}, },
MissingTemplateAfterEmbedder { MissingTemplateAfterEmbedder {
embedder: String, embedder: Token<'a>,
indexing: Vec<String>, indexing: Vec<String>,
search: Vec<String>, search: Vec<String>,
}, },
UnknownTemplatePrefix { UnknownTemplatePrefix {
embedder: String, embedder: Token<'a>,
found: String, found: Token<'a>,
indexing: Vec<String>, indexing: Vec<String>,
search: Vec<String>, search: Vec<String>,
}, },
ReponseError(ResponseError), ReponseError(ResponseError),
MissingFragment { MissingFragment {
embedder: String, embedder: Token<'a>,
kind: FragmentKind, kind: FragmentKind,
available: Vec<String>, available: Vec<String>,
}, },
FragmentDoesNotExist { FragmentDoesNotExist {
embedder: String, embedder: Token<'a>,
fragment: String, fragment: Token<'a>,
kind: FragmentKind, kind: FragmentKind,
available: Vec<String>, available: Vec<String>,
}, },
LeftOverToken(String), LeftOverToken(Token<'a>),
MissingChatCompletionTemplate, MissingChatCompletionTemplate,
UnknownChatCompletionTemplate(String), UnknownChatCompletionTemplate(Token<'a>),
ExpectedDotAfterValue(milli::Span<'a>),
ExpectedValue(milli::Span<'a>),
DocumentNotFound(String), DocumentNotFound(String),
BothInlineDocAndDocId, BothInlineDocAndDocId,
@ -182,13 +185,13 @@ enum RenderError {
CouldNotHandleInput, CouldNotHandleInput,
} }
impl From<heed::Error> for RenderError { impl From<heed::Error> for RenderError<'_> {
fn from(error: heed::Error) -> Self { fn from(error: heed::Error) -> Self {
RenderError::ReponseError(error.into()) RenderError::ReponseError(error.into())
} }
} }
impl From<milli::Error> for RenderError { impl From<milli::Error> for RenderError<'_> {
fn from(error: milli::Error) -> Self { fn from(error: milli::Error) -> Self {
RenderError::ReponseError(error.into()) RenderError::ReponseError(error.into())
} }
@ -196,7 +199,7 @@ impl From<milli::Error> for RenderError {
use RenderError::*; use RenderError::*;
impl From<RenderError> for ResponseError { impl From<RenderError<'_>> for ResponseError {
fn from(error: RenderError) -> Self { fn from(error: RenderError) -> Self {
match error { match error {
MultipleTemplates => ResponseError::from_msg( MultipleTemplates => ResponseError::from_msg(
@ -322,31 +325,39 @@ impl From<RenderError> for ResponseError {
String::from("Could not handle the input provided."), String::from("Could not handle the input provided."),
Code::InvalidRenderInput, Code::InvalidRenderInput,
), ),
ExpectedDotAfterValue(span) => ResponseError::from_msg(
format!("Expected a dot after value, but found `{span}`."),
Code::InvalidRenderTemplateId,
),
ExpectedValue(span) => ResponseError::from_msg(
format!("Expected a value, but found `{span}`."),
Code::InvalidRenderTemplateId,
),
} }
} }
} }
fn parse_template_id_fragment( fn parse_template_id_fragment<'a>(
name: Option<&str>, name: Option<Token<'a>>,
kind: FragmentKind, kind: FragmentKind,
embedding_config: &IndexEmbeddingConfig, embedding_config: &IndexEmbeddingConfig,
embedder_name: &str, embedder: Token<'a>,
) -> Result<serde_json::Value, RenderError> { ) -> Result<serde_json::Value, RenderError<'a>> {
let get_available = let get_available =
[EmbedderOptions::indexing_fragments, EmbedderOptions::search_fragments][kind as usize]; [EmbedderOptions::indexing_fragments, EmbedderOptions::search_fragments][kind as usize];
let get_specific = let get_specific =
[EmbedderOptions::indexing_fragment, EmbedderOptions::search_fragment][kind as usize]; [EmbedderOptions::indexing_fragment, EmbedderOptions::search_fragment][kind as usize];
let fragment_name = name.ok_or_else(|| MissingFragment { let fragment = name.ok_or_else(|| MissingFragment {
embedder: embedder_name.to_string(), embedder: embedder.clone(),
kind, kind,
available: get_available(&embedding_config.config.embedder_options), available: get_available(&embedding_config.config.embedder_options),
})?; })?;
let fragment = get_specific(&embedding_config.config.embedder_options, fragment_name) let fragment = get_specific(&embedding_config.config.embedder_options, fragment.value())
.ok_or_else(|| FragmentDoesNotExist { .ok_or_else(|| FragmentDoesNotExist {
embedder: embedder_name.to_string(), embedder,
fragment: fragment_name.to_string(), fragment,
kind, kind,
available: get_available(&embedding_config.config.embedder_options), available: get_available(&embedding_config.config.embedder_options),
})?; })?;
@ -354,83 +365,96 @@ fn parse_template_id_fragment(
Ok(fragment.clone()) Ok(fragment.clone())
} }
fn parse_template_id( fn parse_template_id<'a>(
index: &Index, index: &Index,
rtxn: &RoTxn<'_>, rtxn: &RoTxn,
id: &str, id: &'a str,
) -> Result<(serde_json::Value, bool), RenderError> { ) -> Result<(serde_json::Value, bool), RenderError<'a>> {
let mut parts = id.split('.'); let mut input: Span = id.into();
let mut next_part = |first: bool| -> Result<Option<Token<'_>>, RenderError<'a>> {
if input.is_empty() {
return Ok(None);
}
if !first {
if !input.starts_with('.') {
return Err(ExpectedDotAfterValue(input));
}
input = milli::filter_parser::Slice::slice(&input, 1..);
}
let (remaining, value) = milli::filter_parser::parse_dotted_value_part(input)
.map_err(|_| ExpectedValue(input))?;
input = remaining;
let root = parts.next().ok_or(EmptyTemplateId)?; Ok(Some(value))
};
let template = match root { let root = next_part(true)?.ok_or(EmptyTemplateId)?;
let template = match root.value() {
"embedders" => { "embedders" => {
let index_embedding_configs = index.embedding_configs(); let index_embedding_configs = index.embedding_configs();
let embedding_configs = index_embedding_configs.embedding_configs(rtxn)?; let embedding_configs = index_embedding_configs.embedding_configs(rtxn)?;
let get_embedders = || embedding_configs.iter().map(|c| c.name.clone()).collect(); let get_embedders = || embedding_configs.iter().map(|c| c.name.clone()).collect();
let embedder = let embedder = next_part(false)?
parts.next().ok_or_else(|| MissingEmbedderName { available: get_embedders() })?; .ok_or_else(|| MissingEmbedderName { available: get_embedders() })?;
let embedding_config = embedding_configs let embedding_config = embedding_configs
.iter() .iter()
.find(|config| config.name == embedder) .find(|config| config.name == embedder.value())
.ok_or_else(|| EmbedderDoesNotExist { .ok_or_else(|| EmbedderDoesNotExist {
embedder: embedder.to_string(), embedder: embedder.clone(),
available: get_embedders(), available: get_embedders(),
})?; })?;
let get_indexing = || embedding_config.config.embedder_options.indexing_fragments(); let get_indexing = || embedding_config.config.embedder_options.indexing_fragments();
let get_search = || embedding_config.config.embedder_options.search_fragments(); let get_search = || embedding_config.config.embedder_options.search_fragments();
let template_kind = parts.next().ok_or_else(|| MissingTemplateAfterEmbedder { let template_kind = next_part(false)?.ok_or_else(|| MissingTemplateAfterEmbedder {
embedder: embedder.to_string(), embedder: embedder.clone(),
indexing: get_indexing(), indexing: get_indexing(),
search: get_search(), search: get_search(),
})?; })?;
match template_kind { match template_kind.value() {
"documentTemplate" | "documenttemplate" "documentTemplate" if !embedding_config.fragments.as_slice().is_empty() => {
if !embedding_config.fragments.as_slice().is_empty() => return Err(EmbedderUsesFragments { embedder });
{
return Err(EmbedderUsesFragments { embedder: embedder.to_string() });
} }
"documentTemplate" | "documenttemplate" => ( "documentTemplate" => (
serde_json::Value::String(embedding_config.config.prompt.template.clone()), serde_json::Value::String(embedding_config.config.prompt.template.clone()),
true, true,
), ),
"indexingFragments" | "indexingfragments" => ( "indexingFragments" => (
parse_template_id_fragment( parse_template_id_fragment(
parts.next(), next_part(false)?,
FragmentKind::Indexing, FragmentKind::Indexing,
embedding_config, embedding_config,
embedder, embedder,
)?, )?,
false, false,
), ),
"searchFragments" | "searchfragments" => ( "searchFragments" => (
parse_template_id_fragment( parse_template_id_fragment(
parts.next(), next_part(false)?,
FragmentKind::Search, FragmentKind::Search,
embedding_config, embedding_config,
embedder, embedder,
)?, )?,
false, false,
), ),
found => { _ => {
return Err(UnknownTemplatePrefix { return Err(UnknownTemplatePrefix {
embedder: embedder.to_string(), embedder,
found: found.to_string(), found: template_kind,
indexing: get_indexing(), indexing: get_indexing(),
search: get_search(), search: get_search(),
}) })
} }
} }
} }
"chatCompletions" | "chatcompletions" => { "chatCompletions" => {
let template_name = parts.next().ok_or(MissingChatCompletionTemplate)?; let template_name = next_part(false)?.ok_or(MissingChatCompletionTemplate)?;
if template_name != "documentTemplate" { if template_name.value() != "documentTemplate" {
return Err(UnknownChatCompletionTemplate(template_name.to_string())); return Err(UnknownChatCompletionTemplate(template_name));
} }
let chat_config = index.chat_config(rtxn)?; let chat_config = index.chat_config(rtxn)?;
@ -438,26 +462,26 @@ fn parse_template_id(
(serde_json::Value::String(chat_config.prompt.template.clone()), true) (serde_json::Value::String(chat_config.prompt.template.clone()), true)
} }
"" => return Err(EmptyTemplateId), "" => return Err(EmptyTemplateId),
unknown => { _ => {
return Err(UnknownTemplateRoot(unknown.to_string())); return Err(UnknownTemplateRoot(root));
} }
}; };
if let Some(next) = parts.next() { if let Some(next) = next_part(false)? {
return Err(LeftOverToken(next.to_string())); return Err(LeftOverToken(next));
} }
Ok(template) Ok(template)
} }
async fn render(index: Index, query: RenderQuery) -> Result<RenderResult, RenderError> { async fn render(index: Index, query: RenderQuery) -> Result<RenderResult, ResponseError> {
let rtxn = index.read_txn()?; let rtxn = index.read_txn()?;
let (template, fields_available) = match (query.template.inline, query.template.id) { let (template, fields_available) = match (query.template.inline, query.template.id) {
(Some(inline), None) => (inline, true), (Some(inline), None) => (inline, true),
(None, Some(id)) => parse_template_id(&index, &rtxn, &id)?, (None, Some(id)) => parse_template_id(&index, &rtxn, &id)?,
(Some(_), Some(_)) => return Err(MultipleTemplates), (Some(_), Some(_)) => return Err(MultipleTemplates.into()),
(None, None) => return Err(MissingTemplate), (None, None) => return Err(MissingTemplate.into()),
}; };
let fields_already_present = query let fields_already_present = query
@ -474,7 +498,7 @@ async fn render(index: Index, query: RenderQuery) -> Result<RenderResult, Render
let insert_fields = let insert_fields =
fields_available && has_doc && fields_probably_used && !fields_already_present; fields_available && has_doc && fields_probably_used && !fields_already_present;
if has_inline_doc && has_document_id { if has_inline_doc && has_document_id {
return Err(BothInlineDocAndDocId); return Err(BothInlineDocAndDocId.into());
} }
let mut rendered = Value::Null; let mut rendered = Value::Null;

View File

@ -26,7 +26,7 @@ async fn wrong_id_prefix() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(value, @r#" snapshot!(value, @r#"
{ {
"message": "Template ID must start with `embedders` or `chatCompletions`, but found `wrong`.", "message": "Template ID must start with `embedders` or `chatCompletions`, but found `{wrong}`.",
"code": "invalid_render_template_id", "code": "invalid_render_template_id",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_render_template_id" "link": "https://docs.meilisearch.com/errors#invalid_render_template_id"
@ -59,7 +59,7 @@ async fn wrong_embedder() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(value, @r#" snapshot!(value, @r#"
{ {
"message": "Embedder `wrong` does not exist.\n Hint: Available embedders are `rest`.", "message": "Embedder `{wrong}` does not exist.\n Hint: Available embedders are `rest`.",
"code": "invalid_render_template_id", "code": "invalid_render_template_id",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_render_template_id" "link": "https://docs.meilisearch.com/errors#invalid_render_template_id"
@ -75,7 +75,7 @@ async fn missing_template_kind() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(value, @r#" snapshot!(value, @r#"
{ {
"message": "Template ID configured with `embedders.rest` but no template kind provided.\n Hint: Available fragments are `indexingFragments.basic`, `indexingFragments.withBreed`, `searchFragments.justBreed`, `searchFragments.justName`, `searchFragments.query`.", "message": "Template ID configured with `embedders.{rest}` but no template kind provided.\n Hint: Available fragments are `indexingFragments.basic`, `indexingFragments.withBreed`, `searchFragments.justBreed`, `searchFragments.justName`, `searchFragments.query`.",
"code": "invalid_render_template_id", "code": "invalid_render_template_id",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_render_template_id" "link": "https://docs.meilisearch.com/errors#invalid_render_template_id"
@ -92,7 +92,7 @@ async fn wrong_template_kind() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(value, @r#" snapshot!(value, @r#"
{ {
"message": "Wrong template `wrong` after embedder `rest`.\n Hint: Available fragments are `indexingFragments.basic`, `indexingFragments.withBreed`, `searchFragments.justBreed`, `searchFragments.justName`, `searchFragments.query`.", "message": "Wrong template `{wrong}` after embedder `{rest}`.\n Hint: Available fragments are `indexingFragments.basic`, `indexingFragments.withBreed`, `searchFragments.justBreed`, `searchFragments.justName`, `searchFragments.query`.",
"code": "invalid_render_template_id", "code": "invalid_render_template_id",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_render_template_id" "link": "https://docs.meilisearch.com/errors#invalid_render_template_id"
@ -109,7 +109,7 @@ async fn document_template_on_fragmented_index() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(value, @r#" snapshot!(value, @r#"
{ {
"message": "Requested document template for embedder `rest` but it uses fragments.\n Hint: Use `indexingFragments` or `searchFragments` instead.", "message": "Requested document template for embedder `{rest}` but it uses fragments.\n Hint: Use `indexingFragments` or `searchFragments` instead.",
"code": "invalid_render_template_id", "code": "invalid_render_template_id",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_render_template_id" "link": "https://docs.meilisearch.com/errors#invalid_render_template_id"
@ -126,7 +126,7 @@ async fn missing_fragment_name() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(value, @r#" snapshot!(value, @r#"
{ {
"message": "Indexing fragment name was not provided.\n Hint: Available indexing fragments for embedder `rest` are `basic`, `withBreed`.", "message": "Indexing fragment name was not provided.\n Hint: Available indexing fragments for embedder `{rest}` are `basic`, `withBreed`.",
"code": "invalid_render_template_id", "code": "invalid_render_template_id",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_render_template_id" "link": "https://docs.meilisearch.com/errors#invalid_render_template_id"
@ -138,7 +138,7 @@ async fn missing_fragment_name() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(value, @r#" snapshot!(value, @r#"
{ {
"message": "Search fragment name was not provided.\n Hint: Available search fragments for embedder `rest` are `justBreed`, `justName`, `query`.", "message": "Search fragment name was not provided.\n Hint: Available search fragments for embedder `{rest}` are `justBreed`, `justName`, `query`.",
"code": "invalid_render_template_id", "code": "invalid_render_template_id",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_render_template_id" "link": "https://docs.meilisearch.com/errors#invalid_render_template_id"
@ -156,7 +156,7 @@ async fn wrong_fragment_name() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(value, @r#" snapshot!(value, @r#"
{ {
"message": "Indexing fragment `wrong` does not exist for embedder `rest`.\n Hint: Available indexing fragments are `basic`, `withBreed`.", "message": "Indexing fragment `{wrong}` does not exist for embedder `{rest}`.\n Hint: Available indexing fragments are `basic`, `withBreed`.",
"code": "invalid_render_template_id", "code": "invalid_render_template_id",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_render_template_id" "link": "https://docs.meilisearch.com/errors#invalid_render_template_id"
@ -168,7 +168,7 @@ async fn wrong_fragment_name() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(value, @r#" snapshot!(value, @r#"
{ {
"message": "Search fragment `wrong` does not exist for embedder `rest`.\n Hint: Available search fragments are `justBreed`, `justName`, `query`.", "message": "Search fragment `{wrong}` does not exist for embedder `{rest}`.\n Hint: Available search fragments are `justBreed`, `justName`, `query`.",
"code": "invalid_render_template_id", "code": "invalid_render_template_id",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_render_template_id" "link": "https://docs.meilisearch.com/errors#invalid_render_template_id"
@ -188,7 +188,7 @@ async fn leftover_tokens() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(value, @r#" snapshot!(value, @r#"
{ {
"message": "Leftover token `leftover` after parsing template ID", "message": "Leftover token `{leftover}` after parsing template ID",
"code": "invalid_render_template_id", "code": "invalid_render_template_id",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_render_template_id" "link": "https://docs.meilisearch.com/errors#invalid_render_template_id"
@ -201,7 +201,7 @@ async fn leftover_tokens() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(value, @r#" snapshot!(value, @r#"
{ {
"message": "Leftover token `leftover` after parsing template ID", "message": "Leftover token `{leftover}` after parsing template ID",
"code": "invalid_render_template_id", "code": "invalid_render_template_id",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_render_template_id" "link": "https://docs.meilisearch.com/errors#invalid_render_template_id"
@ -214,7 +214,7 @@ async fn leftover_tokens() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(value, @r#" snapshot!(value, @r#"
{ {
"message": "Leftover token `leftover` after parsing template ID", "message": "Leftover token `{leftover}` after parsing template ID",
"code": "invalid_render_template_id", "code": "invalid_render_template_id",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_render_template_id" "link": "https://docs.meilisearch.com/errors#invalid_render_template_id"
@ -274,7 +274,7 @@ async fn wrong_chat_completions_template() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(value, @r#" snapshot!(value, @r#"
{ {
"message": "Unknown chat completion template ID `wrong`. The only available template is `documentTemplate`.", "message": "Unknown chat completion template ID `{wrong}`. The only available template is `documentTemplate`.",
"code": "invalid_render_template_id", "code": "invalid_render_template_id",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_render_template_id" "link": "https://docs.meilisearch.com/errors#invalid_render_template_id"
@ -603,7 +603,7 @@ async fn embedder_document_template() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(value, @r#" snapshot!(value, @r#"
{ {
"message": "Wrong template `wrong` after embedder `rest`.\n Hint: Available template: `documentTemplate`.", "message": "Wrong template `{wrong}` after embedder `{rest}`.\n Hint: Available template: `documentTemplate`.",
"code": "invalid_render_template_id", "code": "invalid_render_template_id",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_render_template_id" "link": "https://docs.meilisearch.com/errors#invalid_render_template_id"

View File

@ -44,6 +44,7 @@ use std::hash::BuildHasherDefault;
use charabia::normalizer::{CharNormalizer, CompatibilityDecompositionNormalizer}; use charabia::normalizer::{CharNormalizer, CompatibilityDecompositionNormalizer};
pub use documents::GeoSortStrategy; pub use documents::GeoSortStrategy;
pub use filter_parser;
pub use filter_parser::{Condition, FilterCondition, Span, Token}; pub use filter_parser::{Condition, FilterCondition, Span, Token};
use fxhash::{FxHasher32, FxHasher64}; use fxhash::{FxHasher32, FxHasher64};
pub use grenad::CompressionType; pub use grenad::CompressionType;