From e654eddf56f2890c5caea5d5be3465ffd9e203ab Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Tue, 10 Jun 2025 10:21:34 +0200 Subject: [PATCH] Improve the chat workspace REST endpoints --- crates/index-scheduler/src/lib.rs | 8 +++- crates/meilisearch-auth/src/store.rs | 9 ++-- crates/meilisearch-types/src/keys.rs | 15 ++++--- crates/meilisearch/src/routes/chats/mod.rs | 45 ++++++++++++++++++- .../meilisearch/src/routes/chats/settings.rs | 14 +++--- 5 files changed, 72 insertions(+), 19 deletions(-) diff --git a/crates/index-scheduler/src/lib.rs b/crates/index-scheduler/src/lib.rs index 3860ef5cb..3ad342bea 100644 --- a/crates/index-scheduler/src/lib.rs +++ b/crates/index-scheduler/src/lib.rs @@ -55,7 +55,7 @@ use meilisearch_types::features::{ ChatCompletionSettings, InstanceTogglableFeatures, Network, RuntimeTogglableFeatures, }; use meilisearch_types::heed::byteorder::BE; -use meilisearch_types::heed::types::{SerdeJson, Str, I128}; +use meilisearch_types::heed::types::{DecodeIgnore, SerdeJson, Str, I128}; use meilisearch_types::heed::{self, Database, Env, RoTxn, RwTxn, WithoutTls}; use meilisearch_types::milli::index::IndexEmbeddingConfig; use meilisearch_types::milli::update::IndexerConfig; @@ -904,6 +904,12 @@ impl IndexScheduler { self.chat_settings.get(rtxn, uid).map_err(Into::into) } + /// Return true if chat workspace exists. + pub fn chat_workspace_exists(&self, name: &str) -> Result { + let rtxn = self.env.read_txn()?; + Ok(self.chat_settings.remap_data_type::().get(&rtxn, name)?.is_some()) + } + pub fn put_chat_settings( &self, wtxn: &mut RwTxn, diff --git a/crates/meilisearch-auth/src/store.rs b/crates/meilisearch-auth/src/store.rs index e20f259ee..bae27afe4 100644 --- a/crates/meilisearch-auth/src/store.rs +++ b/crates/meilisearch-auth/src/store.rs @@ -125,12 +125,11 @@ impl HeedAuthStore { Action::MetricsAll => { actions.insert(Action::MetricsGet); } + Action::ChatsAll => { + actions.extend([Action::ChatsGet, Action::ChatsDelete]); + } Action::ChatsSettingsAll => { - actions.extend([ - Action::ChatsSettingsGet, - Action::ChatsSettingsUpdate, - Action::ChatsSettingsDelete, - ]); + actions.extend([Action::ChatsSettingsGet, Action::ChatsSettingsUpdate]); } other => { actions.insert(*other); diff --git a/crates/meilisearch-types/src/keys.rs b/crates/meilisearch-types/src/keys.rs index fe6005796..df2810727 100644 --- a/crates/meilisearch-types/src/keys.rs +++ b/crates/meilisearch-types/src/keys.rs @@ -326,9 +326,15 @@ pub enum Action { #[serde(rename = "chatCompletions")] #[deserr(rename = "chatCompletions")] ChatCompletions, + #[serde(rename = "chats.*")] + #[deserr(rename = "chats.*")] + ChatsAll, #[serde(rename = "chats.get")] #[deserr(rename = "chats.get")] ChatsGet, + #[serde(rename = "chats.delete")] + #[deserr(rename = "chats.delete")] + ChatsDelete, #[serde(rename = "chatsSettings.*")] #[deserr(rename = "chatsSettings.*")] ChatsSettingsAll, @@ -338,9 +344,6 @@ pub enum Action { #[serde(rename = "chatsSettings.update")] #[deserr(rename = "chatsSettings.update")] ChatsSettingsUpdate, - #[serde(rename = "chatsSettings.delete")] - #[deserr(rename = "chatsSettings.delete")] - ChatsSettingsDelete, } impl Action { @@ -367,11 +370,12 @@ impl Action { SETTINGS_GET => Some(Self::SettingsGet), SETTINGS_UPDATE => Some(Self::SettingsUpdate), CHAT_COMPLETIONS => Some(Self::ChatCompletions), + CHATS_ALL => Some(Self::ChatsAll), CHATS_GET => Some(Self::ChatsGet), + CHATS_DELETE => Some(Self::ChatsDelete), CHATS_SETTINGS_ALL => Some(Self::ChatsSettingsAll), CHATS_SETTINGS_GET => Some(Self::ChatsSettingsGet), CHATS_SETTINGS_UPDATE => Some(Self::ChatsSettingsUpdate), - CHATS_SETTINGS_DELETE => Some(Self::ChatsSettingsDelete), STATS_ALL => Some(Self::StatsAll), STATS_GET => Some(Self::StatsGet), METRICS_ALL => Some(Self::MetricsAll), @@ -438,9 +442,10 @@ pub mod actions { pub const NETWORK_UPDATE: u8 = NetworkUpdate.repr(); pub const CHAT_COMPLETIONS: u8 = ChatCompletions.repr(); + pub const CHATS_ALL: u8 = ChatsAll.repr(); pub const CHATS_GET: u8 = ChatsGet.repr(); + pub const CHATS_DELETE: u8 = ChatsDelete.repr(); pub const CHATS_SETTINGS_ALL: u8 = ChatsSettingsAll.repr(); pub const CHATS_SETTINGS_GET: u8 = ChatsSettingsGet.repr(); pub const CHATS_SETTINGS_UPDATE: u8 = ChatsSettingsUpdate.repr(); - pub const CHATS_SETTINGS_DELETE: u8 = ChatsSettingsDelete.repr(); } diff --git a/crates/meilisearch/src/routes/chats/mod.rs b/crates/meilisearch/src/routes/chats/mod.rs index ddaf4d80d..8c8c9bc32 100644 --- a/crates/meilisearch/src/routes/chats/mod.rs +++ b/crates/meilisearch/src/routes/chats/mod.rs @@ -6,9 +6,11 @@ use index_scheduler::IndexScheduler; use meilisearch_types::deserr::query_params::Param; use meilisearch_types::deserr::DeserrQueryParamError; use meilisearch_types::error::deserr_codes::{InvalidIndexLimit, InvalidIndexOffset}; -use meilisearch_types::error::ResponseError; +use meilisearch_types::error::{Code, ResponseError}; +use meilisearch_types::index_uid::IndexUid; use meilisearch_types::keys::actions; use serde::{Deserialize, Serialize}; +use serde_json::json; use tracing::debug; use utoipa::{IntoParams, ToSchema}; @@ -40,11 +42,52 @@ pub struct ChatsParam { pub fn configure(cfg: &mut web::ServiceConfig) { cfg.service(web::resource("").route(web::get().to(list_workspaces))).service( web::scope("/{workspace_uid}") + .service( + web::resource("") + .route(web::get().to(get_chat)) + .route(web::delete().to(delete_chat)), + ) .service(web::scope("/chat/completions").configure(chat_completions::configure)) .service(web::scope("/settings").configure(settings::configure)), ); } +pub async fn get_chat( + index_scheduler: GuardedData, Data>, + workspace_uid: web::Path, +) -> Result { + index_scheduler.features().check_chat_completions("displaying a chat")?; + + let workspace_uid = IndexUid::try_from(workspace_uid.into_inner())?; + if index_scheduler.chat_workspace_exists(&workspace_uid)? { + Ok(HttpResponse::Ok().json(json!({ "uid": workspace_uid }))) + } else { + Err(ResponseError::from_msg( + format!("chat {workspace_uid} not found"), + Code::ChatWorkspaceNotFound, + )) + } +} + +pub async fn delete_chat( + index_scheduler: GuardedData, Data>, + workspace_uid: web::Path, +) -> Result { + index_scheduler.features().check_chat_completions("deleting a chat")?; + + let mut wtxn = index_scheduler.write_txn()?; + let workspace_uid = workspace_uid.into_inner(); + if index_scheduler.delete_chat_settings(&mut wtxn, &workspace_uid)? { + wtxn.commit()?; + Ok(HttpResponse::NoContent().finish()) + } else { + Err(ResponseError::from_msg( + format!("chat {workspace_uid} not found"), + Code::ChatWorkspaceNotFound, + )) + } +} + #[derive(Deserr, Debug, Clone, Copy, IntoParams)] #[deserr(error = DeserrQueryParamError, rename_all = camelCase, deny_unknown_fields)] #[into_params(rename_all = "camelCase", parameter_in = Query)] diff --git a/crates/meilisearch/src/routes/chats/settings.rs b/crates/meilisearch/src/routes/chats/settings.rs index dae2826fe..329732e75 100644 --- a/crates/meilisearch/src/routes/chats/settings.rs +++ b/crates/meilisearch/src/routes/chats/settings.rs @@ -26,7 +26,7 @@ pub fn configure(cfg: &mut web::ServiceConfig) { web::resource("") .route(web::get().to(SeqHandler(get_settings))) .route(web::patch().to(SeqHandler(patch_settings))) - .route(web::delete().to(SeqHandler(delete_settings))), + .route(web::delete().to(SeqHandler(reset_settings))), ); } @@ -159,9 +159,9 @@ async fn patch_settings( Ok(HttpResponse::Ok().json(settings)) } -async fn delete_settings( +async fn reset_settings( index_scheduler: GuardedData< - ActionPolicy<{ actions::CHATS_SETTINGS_DELETE }>, + ActionPolicy<{ actions::CHATS_SETTINGS_UPDATE }>, Data, >, chats_param: web::Path, @@ -169,12 +169,12 @@ async fn delete_settings( index_scheduler.features().check_chat_completions("using the /chats/settings route")?; let ChatsParam { workspace_uid } = chats_param.into_inner(); - - // TODO do a spawn_blocking here let mut wtxn = index_scheduler.write_txn()?; - if index_scheduler.delete_chat_settings(&mut wtxn, &workspace_uid)? { + if index_scheduler.chat_settings(&wtxn, &workspace_uid)?.is_some() { + let settings = Default::default(); + index_scheduler.put_chat_settings(&mut wtxn, &workspace_uid, &settings)?; wtxn.commit()?; - Ok(HttpResponse::NoContent().finish()) + Ok(HttpResponse::Ok().json(settings)) } else { Err(ResponseError::from_msg( format!("Chat `{workspace_uid}` not found"),