mirror of
https://github.com/meilisearch/MeiliSearch
synced 2025-06-15 12:31:35 +02:00
Better support for Mistral errors
This commit is contained in:
parent
596617dd31
commit
9ae73e3c05
@ -107,7 +107,7 @@ impl ChatCompletionSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub enum ChatCompletionSource {
|
pub enum ChatCompletionSource {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -23,7 +23,10 @@ use futures::StreamExt;
|
|||||||
use index_scheduler::IndexScheduler;
|
use index_scheduler::IndexScheduler;
|
||||||
use meilisearch_auth::AuthController;
|
use meilisearch_auth::AuthController;
|
||||||
use meilisearch_types::error::{Code, ResponseError};
|
use meilisearch_types::error::{Code, ResponseError};
|
||||||
use meilisearch_types::features::{ChatCompletionPrompts as DbChatCompletionPrompts, SystemRole};
|
use meilisearch_types::features::{
|
||||||
|
ChatCompletionPrompts as DbChatCompletionPrompts,
|
||||||
|
ChatCompletionSource as DbChatCompletionSource, SystemRole,
|
||||||
|
};
|
||||||
use meilisearch_types::keys::actions;
|
use meilisearch_types::keys::actions;
|
||||||
use meilisearch_types::milli::index::ChatConfig;
|
use meilisearch_types::milli::index::ChatConfig;
|
||||||
use meilisearch_types::milli::{all_obkv_to_json, obkv_to_json, TimeBudget};
|
use meilisearch_types::milli::{all_obkv_to_json, obkv_to_json, TimeBudget};
|
||||||
@ -34,7 +37,7 @@ use tokio::runtime::Handle;
|
|||||||
use tokio::sync::mpsc::error::SendError;
|
use tokio::sync::mpsc::error::SendError;
|
||||||
|
|
||||||
use super::config::Config;
|
use super::config::Config;
|
||||||
use super::errors::StreamErrorEvent;
|
use super::errors::{MistralError, OpenAiOutsideError, StreamErrorEvent};
|
||||||
use super::utils::format_documents;
|
use super::utils::format_documents;
|
||||||
use super::{
|
use super::{
|
||||||
ChatsParam, MEILI_APPEND_CONVERSATION_MESSAGE_NAME, MEILI_SEARCH_IN_INDEX_FUNCTION_NAME,
|
ChatsParam, MEILI_APPEND_CONVERSATION_MESSAGE_NAME, MEILI_SEARCH_IN_INDEX_FUNCTION_NAME,
|
||||||
@ -469,6 +472,7 @@ async fn streamed_chat(
|
|||||||
&search_queue,
|
&search_queue,
|
||||||
&auth_token,
|
&auth_token,
|
||||||
&client,
|
&client,
|
||||||
|
chat_settings.source,
|
||||||
&mut chat_completion,
|
&mut chat_completion,
|
||||||
&tx,
|
&tx,
|
||||||
&mut global_tool_calls,
|
&mut global_tool_calls,
|
||||||
@ -501,6 +505,7 @@ async fn run_conversation<C: async_openai::config::Config>(
|
|||||||
search_queue: &web::Data<SearchQueue>,
|
search_queue: &web::Data<SearchQueue>,
|
||||||
auth_token: &str,
|
auth_token: &str,
|
||||||
client: &Client<C>,
|
client: &Client<C>,
|
||||||
|
source: DbChatCompletionSource,
|
||||||
chat_completion: &mut CreateChatCompletionRequest,
|
chat_completion: &mut CreateChatCompletionRequest,
|
||||||
tx: &SseEventSender,
|
tx: &SseEventSender,
|
||||||
global_tool_calls: &mut HashMap<u32, Call>,
|
global_tool_calls: &mut HashMap<u32, Call>,
|
||||||
@ -595,7 +600,13 @@ async fn run_conversation<C: async_openai::config::Config>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let error = StreamErrorEvent::from_openai_error(error).await.unwrap();
|
let result = match source {
|
||||||
|
DbChatCompletionSource::Mistral => {
|
||||||
|
StreamErrorEvent::from_openai_error::<MistralError>(error).await
|
||||||
|
}
|
||||||
|
_ => StreamErrorEvent::from_openai_error::<OpenAiOutsideError>(error).await,
|
||||||
|
};
|
||||||
|
let error = result.unwrap_or_else(StreamErrorEvent::from_reqwest_error);
|
||||||
tx.send_error(&error).await?;
|
tx.send_error(&error).await?;
|
||||||
return Ok(ControlFlow::Break(None));
|
return Ok(ControlFlow::Break(None));
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,39 @@ use meilisearch_types::error::ResponseError;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
/// The error type which is always `error`.
|
||||||
|
const ERROR_TYPE: &str = "error";
|
||||||
|
|
||||||
|
/// The error struct returned by the Mistral API.
|
||||||
|
///
|
||||||
|
/// ```json
|
||||||
|
/// {
|
||||||
|
/// "object": "error",
|
||||||
|
/// "message": "Service tier capacity exceeded for this model.",
|
||||||
|
/// "type": "invalid_request_error",
|
||||||
|
/// "param": null,
|
||||||
|
/// "code": null
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct MistralError {
|
||||||
|
message: String,
|
||||||
|
r#type: String,
|
||||||
|
param: Option<String>,
|
||||||
|
code: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<MistralError> for StreamErrorEvent {
|
||||||
|
fn from(error: MistralError) -> Self {
|
||||||
|
let MistralError { message, r#type, param, code } = error;
|
||||||
|
StreamErrorEvent {
|
||||||
|
event_id: Uuid::new_v4().to_string(),
|
||||||
|
r#type: ERROR_TYPE.to_owned(),
|
||||||
|
error: StreamError { r#type, code, message, param, event_id: None },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct OpenAiOutsideError {
|
pub struct OpenAiOutsideError {
|
||||||
/// Emitted when an error occurs.
|
/// Emitted when an error occurs.
|
||||||
@ -23,6 +56,17 @@ pub struct OpenAiInnerError {
|
|||||||
r#type: String,
|
r#type: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<OpenAiOutsideError> for StreamErrorEvent {
|
||||||
|
fn from(error: OpenAiOutsideError) -> Self {
|
||||||
|
let OpenAiOutsideError { error: OpenAiInnerError { code, message, param, r#type } } = error;
|
||||||
|
StreamErrorEvent {
|
||||||
|
event_id: Uuid::new_v4().to_string(),
|
||||||
|
r#type: ERROR_TYPE.to_string(),
|
||||||
|
error: StreamError { r#type, code, message, param, event_id: None },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An error that occurs during the streaming process.
|
/// An error that occurs during the streaming process.
|
||||||
///
|
///
|
||||||
/// It directly comes from the OpenAI API and you can
|
/// It directly comes from the OpenAI API and you can
|
||||||
@ -54,13 +98,15 @@ pub struct StreamError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl StreamErrorEvent {
|
impl StreamErrorEvent {
|
||||||
const ERROR_TYPE: &str = "error";
|
pub async fn from_openai_error<E>(error: OpenAIError) -> Result<Self, reqwest::Error>
|
||||||
|
where
|
||||||
pub async fn from_openai_error(error: OpenAIError) -> Result<Self, reqwest::Error> {
|
E: serde::de::DeserializeOwned,
|
||||||
|
Self: From<E>,
|
||||||
|
{
|
||||||
match error {
|
match error {
|
||||||
OpenAIError::Reqwest(e) => Ok(StreamErrorEvent {
|
OpenAIError::Reqwest(e) => Ok(StreamErrorEvent {
|
||||||
event_id: Uuid::new_v4().to_string(),
|
event_id: Uuid::new_v4().to_string(),
|
||||||
r#type: Self::ERROR_TYPE.to_string(),
|
r#type: ERROR_TYPE.to_string(),
|
||||||
error: StreamError {
|
error: StreamError {
|
||||||
r#type: "internal_reqwest_error".to_string(),
|
r#type: "internal_reqwest_error".to_string(),
|
||||||
code: Some("internal".to_string()),
|
code: Some("internal".to_string()),
|
||||||
@ -71,7 +117,7 @@ impl StreamErrorEvent {
|
|||||||
}),
|
}),
|
||||||
OpenAIError::ApiError(ApiError { message, r#type, param, code }) => {
|
OpenAIError::ApiError(ApiError { message, r#type, param, code }) => {
|
||||||
Ok(StreamErrorEvent {
|
Ok(StreamErrorEvent {
|
||||||
r#type: Self::ERROR_TYPE.to_string(),
|
r#type: ERROR_TYPE.to_string(),
|
||||||
event_id: Uuid::new_v4().to_string(),
|
event_id: Uuid::new_v4().to_string(),
|
||||||
error: StreamError {
|
error: StreamError {
|
||||||
r#type: r#type.unwrap_or_else(|| "unknown".to_string()),
|
r#type: r#type.unwrap_or_else(|| "unknown".to_string()),
|
||||||
@ -84,7 +130,7 @@ impl StreamErrorEvent {
|
|||||||
}
|
}
|
||||||
OpenAIError::JSONDeserialize(error) => Ok(StreamErrorEvent {
|
OpenAIError::JSONDeserialize(error) => Ok(StreamErrorEvent {
|
||||||
event_id: Uuid::new_v4().to_string(),
|
event_id: Uuid::new_v4().to_string(),
|
||||||
r#type: Self::ERROR_TYPE.to_string(),
|
r#type: ERROR_TYPE.to_string(),
|
||||||
error: StreamError {
|
error: StreamError {
|
||||||
r#type: "json_deserialize_error".to_string(),
|
r#type: "json_deserialize_error".to_string(),
|
||||||
code: Some("internal".to_string()),
|
code: Some("internal".to_string()),
|
||||||
@ -96,30 +142,16 @@ impl StreamErrorEvent {
|
|||||||
OpenAIError::FileSaveError(_) | OpenAIError::FileReadError(_) => unreachable!(),
|
OpenAIError::FileSaveError(_) | OpenAIError::FileReadError(_) => unreachable!(),
|
||||||
OpenAIError::StreamError(error) => match error {
|
OpenAIError::StreamError(error) => match error {
|
||||||
EventSourceError::InvalidStatusCode(_status_code, response) => {
|
EventSourceError::InvalidStatusCode(_status_code, response) => {
|
||||||
let OpenAiOutsideError {
|
let error = response.json::<E>().await?;
|
||||||
error: OpenAiInnerError { code, message, param, r#type },
|
Ok(StreamErrorEvent::from(error))
|
||||||
} = response.json().await?;
|
|
||||||
|
|
||||||
Ok(StreamErrorEvent {
|
|
||||||
event_id: Uuid::new_v4().to_string(),
|
|
||||||
r#type: Self::ERROR_TYPE.to_string(),
|
|
||||||
error: StreamError { r#type, code, message, param, event_id: None },
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
EventSourceError::InvalidContentType(_header_value, response) => {
|
EventSourceError::InvalidContentType(_header_value, response) => {
|
||||||
let OpenAiOutsideError {
|
let error = response.json::<E>().await?;
|
||||||
error: OpenAiInnerError { code, message, param, r#type },
|
Ok(StreamErrorEvent::from(error))
|
||||||
} = response.json().await?;
|
|
||||||
|
|
||||||
Ok(StreamErrorEvent {
|
|
||||||
event_id: Uuid::new_v4().to_string(),
|
|
||||||
r#type: Self::ERROR_TYPE.to_string(),
|
|
||||||
error: StreamError { r#type, code, message, param, event_id: None },
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
EventSourceError::Utf8(error) => Ok(StreamErrorEvent {
|
EventSourceError::Utf8(error) => Ok(StreamErrorEvent {
|
||||||
event_id: Uuid::new_v4().to_string(),
|
event_id: Uuid::new_v4().to_string(),
|
||||||
r#type: Self::ERROR_TYPE.to_string(),
|
r#type: ERROR_TYPE.to_string(),
|
||||||
error: StreamError {
|
error: StreamError {
|
||||||
r#type: "invalid_utf8_error".to_string(),
|
r#type: "invalid_utf8_error".to_string(),
|
||||||
code: None,
|
code: None,
|
||||||
@ -130,7 +162,7 @@ impl StreamErrorEvent {
|
|||||||
}),
|
}),
|
||||||
EventSourceError::Parser(error) => Ok(StreamErrorEvent {
|
EventSourceError::Parser(error) => Ok(StreamErrorEvent {
|
||||||
event_id: Uuid::new_v4().to_string(),
|
event_id: Uuid::new_v4().to_string(),
|
||||||
r#type: Self::ERROR_TYPE.to_string(),
|
r#type: ERROR_TYPE.to_string(),
|
||||||
error: StreamError {
|
error: StreamError {
|
||||||
r#type: "parser_error".to_string(),
|
r#type: "parser_error".to_string(),
|
||||||
code: None,
|
code: None,
|
||||||
@ -141,7 +173,7 @@ impl StreamErrorEvent {
|
|||||||
}),
|
}),
|
||||||
EventSourceError::Transport(error) => Ok(StreamErrorEvent {
|
EventSourceError::Transport(error) => Ok(StreamErrorEvent {
|
||||||
event_id: Uuid::new_v4().to_string(),
|
event_id: Uuid::new_v4().to_string(),
|
||||||
r#type: Self::ERROR_TYPE.to_string(),
|
r#type: ERROR_TYPE.to_string(),
|
||||||
error: StreamError {
|
error: StreamError {
|
||||||
r#type: "transport_error".to_string(),
|
r#type: "transport_error".to_string(),
|
||||||
code: None,
|
code: None,
|
||||||
@ -152,7 +184,7 @@ impl StreamErrorEvent {
|
|||||||
}),
|
}),
|
||||||
EventSourceError::InvalidLastEventId(message) => Ok(StreamErrorEvent {
|
EventSourceError::InvalidLastEventId(message) => Ok(StreamErrorEvent {
|
||||||
event_id: Uuid::new_v4().to_string(),
|
event_id: Uuid::new_v4().to_string(),
|
||||||
r#type: Self::ERROR_TYPE.to_string(),
|
r#type: ERROR_TYPE.to_string(),
|
||||||
error: StreamError {
|
error: StreamError {
|
||||||
r#type: "invalid_last_event_id".to_string(),
|
r#type: "invalid_last_event_id".to_string(),
|
||||||
code: None,
|
code: None,
|
||||||
@ -163,7 +195,7 @@ impl StreamErrorEvent {
|
|||||||
}),
|
}),
|
||||||
EventSourceError::StreamEnded => Ok(StreamErrorEvent {
|
EventSourceError::StreamEnded => Ok(StreamErrorEvent {
|
||||||
event_id: Uuid::new_v4().to_string(),
|
event_id: Uuid::new_v4().to_string(),
|
||||||
r#type: Self::ERROR_TYPE.to_string(),
|
r#type: ERROR_TYPE.to_string(),
|
||||||
error: StreamError {
|
error: StreamError {
|
||||||
r#type: "stream_ended".to_string(),
|
r#type: "stream_ended".to_string(),
|
||||||
code: None,
|
code: None,
|
||||||
@ -175,7 +207,7 @@ impl StreamErrorEvent {
|
|||||||
},
|
},
|
||||||
OpenAIError::InvalidArgument(message) => Ok(StreamErrorEvent {
|
OpenAIError::InvalidArgument(message) => Ok(StreamErrorEvent {
|
||||||
event_id: Uuid::new_v4().to_string(),
|
event_id: Uuid::new_v4().to_string(),
|
||||||
r#type: Self::ERROR_TYPE.to_string(),
|
r#type: ERROR_TYPE.to_string(),
|
||||||
error: StreamError {
|
error: StreamError {
|
||||||
r#type: "invalid_argument".to_string(),
|
r#type: "invalid_argument".to_string(),
|
||||||
code: None,
|
code: None,
|
||||||
@ -191,7 +223,7 @@ impl StreamErrorEvent {
|
|||||||
let ResponseError { code, message, .. } = error;
|
let ResponseError { code, message, .. } = error;
|
||||||
StreamErrorEvent {
|
StreamErrorEvent {
|
||||||
event_id: Uuid::new_v4().to_string(),
|
event_id: Uuid::new_v4().to_string(),
|
||||||
r#type: Self::ERROR_TYPE.to_string(),
|
r#type: ERROR_TYPE.to_string(),
|
||||||
error: StreamError {
|
error: StreamError {
|
||||||
r#type: "response_error".to_string(),
|
r#type: "response_error".to_string(),
|
||||||
code: Some(code.as_str().to_string()),
|
code: Some(code.as_str().to_string()),
|
||||||
@ -201,4 +233,18 @@ impl StreamErrorEvent {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_reqwest_error(error: reqwest::Error) -> Self {
|
||||||
|
StreamErrorEvent {
|
||||||
|
event_id: Uuid::new_v4().to_string(),
|
||||||
|
r#type: ERROR_TYPE.to_string(),
|
||||||
|
error: StreamError {
|
||||||
|
r#type: "reqwest_error".to_string(),
|
||||||
|
code: None,
|
||||||
|
message: error.to_string(),
|
||||||
|
param: None,
|
||||||
|
event_id: None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user