Introduce the first version of the /chat route that mimics the OpenAI API

This commit is contained in:
Clément Renault 2025-05-13 11:19:32 +02:00
parent 5d0d12dfbd
commit 5400f3941a
No known key found for this signature in database
GPG key ID: F250A4C4E3AE5F5F
6 changed files with 251 additions and 5 deletions

View file

@ -31,7 +31,7 @@ anyhow = "1.0.95"
bytes = "1.9.0"
convert_case = "0.6.0"
flate2 = "1.0.35"
reqwest = { version = "0.12.12", features = ["blocking", "rustls-tls"], default-features = false }
reqwest = { version = "0.12.15", features = ["blocking", "rustls-tls"], default-features = false }
[features]
default = ["milli/all-tokenizations"]

View file

@ -308,6 +308,9 @@ pub enum Action {
#[serde(rename = "network.update")]
#[deserr(rename = "network.update")]
NetworkUpdate,
#[serde(rename = "chat.get")]
#[deserr(rename = "chat.get")]
ChatGet,
}
impl Action {
@ -349,6 +352,7 @@ impl Action {
EXPERIMENTAL_FEATURES_UPDATE => Some(Self::ExperimentalFeaturesUpdate),
NETWORK_GET => Some(Self::NetworkGet),
NETWORK_UPDATE => Some(Self::NetworkUpdate),
CHAT_GET => Some(Self::ChatGet),
_otherwise => None,
}
}
@ -397,4 +401,6 @@ pub mod actions {
pub const NETWORK_GET: u8 = NetworkGet.repr();
pub const NETWORK_UPDATE: u8 = NetworkUpdate.repr();
pub const CHAT_GET: u8 = ChatGet.repr();
}

View file

@ -111,6 +111,7 @@ utoipa = { version = "5.3.1", features = [
"openapi_extensions",
] }
utoipa-scalar = { version = "0.3.0", optional = true, features = ["actix-web"] }
async-openai = "0.28.1"
[dev-dependencies]
actix-rt = "2.10.0"

View file

@ -0,0 +1,32 @@
use actix_web::web::{self, Data};
use actix_web::HttpResponse;
use async_openai::config::OpenAIConfig;
use async_openai::types::CreateChatCompletionRequest;
use async_openai::Client;
use index_scheduler::IndexScheduler;
use meilisearch_types::error::ResponseError;
use meilisearch_types::keys::actions;
use crate::extractors::authentication::policies::ActionPolicy;
use crate::extractors::authentication::GuardedData;
pub fn configure(cfg: &mut web::ServiceConfig) {
cfg.service(web::resource("").route(web::post().to(chat)));
}
/// Get a chat completion
async fn chat(
_index_scheduler: GuardedData<ActionPolicy<{ actions::CHAT_GET }>, Data<IndexScheduler>>,
web::Json(chat_completion): web::Json<CreateChatCompletionRequest>,
) -> Result<HttpResponse, ResponseError> {
// To enable later on, when the feature will be experimental
// index_scheduler.features().check_chat("Using the /chat route")?;
let api_key = std::env::var("MEILI_OPENAI_API_KEY")
.expect("cannot find OpenAI API Key (MEILI_OPENAI_API_KEY)");
let config = OpenAIConfig::default().with_api_key(&api_key); // we can also change the API base
let client = Client::with_config(config);
let response = client.chat().create(chat_completion).await.unwrap();
Ok(HttpResponse::Ok().json(response))
}

View file

@ -52,6 +52,7 @@ const PAGINATION_DEFAULT_LIMIT_FN: fn() -> usize = || 20;
mod api_key;
pub mod batches;
pub mod chat;
mod dump;
pub mod features;
pub mod indexes;
@ -113,7 +114,8 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
.service(web::scope("/swap-indexes").configure(swap_indexes::configure))
.service(web::scope("/metrics").configure(metrics::configure))
.service(web::scope("/experimental-features").configure(features::configure))
.service(web::scope("/network").configure(network::configure));
.service(web::scope("/network").configure(network::configure))
.service(web::scope("/chat").configure(chat::configure));
#[cfg(feature = "swagger")]
{