mirror of
https://github.com/perstarkse/minne.git
synced 2026-04-17 22:49:43 +02:00
multi chat and history, oob swap
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
pub mod message_response_stream;
|
||||
pub mod references;
|
||||
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
@@ -16,7 +17,7 @@ use crate::{
|
||||
page_data,
|
||||
server::{routes::html::render_template, AppState},
|
||||
storage::{
|
||||
db::store_item,
|
||||
db::{get_item, store_item},
|
||||
types::{
|
||||
conversation::Conversation,
|
||||
message::{Message, MessageRole},
|
||||
@@ -46,7 +47,7 @@ where
|
||||
page_data!(ChatData, "chat/base.html", {
|
||||
user: User,
|
||||
history: Vec<Message>,
|
||||
conversation: Conversation,
|
||||
conversation: Option<Conversation>,
|
||||
conversation_archive: Vec<Conversation>
|
||||
});
|
||||
|
||||
@@ -101,7 +102,7 @@ pub async fn show_initialized_chat(
|
||||
history: messages,
|
||||
user,
|
||||
conversation_archive,
|
||||
conversation: conversation.clone(),
|
||||
conversation: Some(conversation.clone()),
|
||||
},
|
||||
state.templates.clone(),
|
||||
)?;
|
||||
@@ -129,15 +130,13 @@ pub async fn show_chat_base(
|
||||
.await
|
||||
.map_err(|e| HtmlError::new(e, state.templates.clone()))?;
|
||||
|
||||
let conversation = Conversation::new(user.id.clone(), "New Chat".to_string());
|
||||
|
||||
let output = render_template(
|
||||
ChatData::template_name(),
|
||||
ChatData {
|
||||
history: vec![],
|
||||
user,
|
||||
conversation_archive,
|
||||
conversation,
|
||||
conversation: None,
|
||||
},
|
||||
state.templates.clone(),
|
||||
)?;
|
||||
@@ -179,7 +178,7 @@ pub async fn show_existing_chat(
|
||||
ChatData {
|
||||
history: messages,
|
||||
user,
|
||||
conversation: conversation.clone(),
|
||||
conversation: Some(conversation.clone()),
|
||||
conversation_archive,
|
||||
},
|
||||
state.templates.clone(),
|
||||
@@ -194,11 +193,28 @@ pub async fn new_user_message(
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
Form(form): Form<NewMessageForm>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
let _user = match auth.current_user {
|
||||
let user = match auth.current_user {
|
||||
Some(user) => user,
|
||||
None => return Ok(Redirect::to("/").into_response()),
|
||||
};
|
||||
|
||||
let conversation: Conversation = get_item(&state.surreal_db_client, &conversation_id)
|
||||
.await
|
||||
.map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?
|
||||
.ok_or_else(|| {
|
||||
HtmlError::new(
|
||||
AppError::NotFound("Conversation was not found".to_string()),
|
||||
state.templates.clone(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if conversation.user_id != user.id {
|
||||
return Err(HtmlError::new(
|
||||
AppError::Auth("The user does not have permission for this conversation".to_string()),
|
||||
state.templates.clone(),
|
||||
));
|
||||
};
|
||||
|
||||
let user_message = Message::new(conversation_id, MessageRole::User, form.content, None);
|
||||
|
||||
store_item(&state.surreal_db_client, user_message.clone())
|
||||
@@ -216,5 +232,58 @@ pub async fn new_user_message(
|
||||
state.templates.clone(),
|
||||
)?;
|
||||
|
||||
Ok(output.into_response())
|
||||
let mut response = output.into_response();
|
||||
response.headers_mut().insert(
|
||||
"HX-Push",
|
||||
HeaderValue::from_str(&format!("/chat/{}", conversation.id)).unwrap(),
|
||||
);
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
pub async fn new_chat_user_message(
|
||||
State(state): State<AppState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
Form(form): Form<NewMessageForm>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
let user = match auth.current_user {
|
||||
Some(user) => user,
|
||||
None => return Ok(Redirect::to("/").into_response()),
|
||||
};
|
||||
|
||||
let conversation = Conversation::new(user.id, "New chat".to_string());
|
||||
let user_message = Message::new(
|
||||
conversation.id.clone(),
|
||||
MessageRole::User,
|
||||
form.content,
|
||||
None,
|
||||
);
|
||||
|
||||
store_item(&state.surreal_db_client, conversation.clone())
|
||||
.await
|
||||
.map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?;
|
||||
store_item(&state.surreal_db_client, user_message.clone())
|
||||
.await
|
||||
.map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct SSEResponseInitData {
|
||||
user_message: Message,
|
||||
conversation: Conversation,
|
||||
}
|
||||
|
||||
let output = render_template(
|
||||
"chat/new_chat_first_response.html",
|
||||
SSEResponseInitData {
|
||||
user_message,
|
||||
conversation: conversation.clone(),
|
||||
},
|
||||
state.templates.clone(),
|
||||
)?;
|
||||
|
||||
let mut response = output.into_response();
|
||||
response.headers_mut().insert(
|
||||
"HX-Push",
|
||||
HeaderValue::from_str(&format!("/chat/{}", conversation.id)).unwrap(),
|
||||
);
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
62
src/server/routes/html/chat/references.rs
Normal file
62
src/server/routes/html/chat/references.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
response::{IntoResponse, Redirect},
|
||||
};
|
||||
use axum_session_auth::AuthSession;
|
||||
use axum_session_surreal::SessionSurrealPool;
|
||||
use serde::Serialize;
|
||||
use surrealdb::{engine::any::Any, Surreal};
|
||||
use tracing::info;
|
||||
|
||||
use crate::{
|
||||
error::{AppError, HtmlError},
|
||||
server::{routes::html::render_template, AppState},
|
||||
storage::{
|
||||
db::get_item,
|
||||
types::{knowledge_entity::KnowledgeEntity, user::User},
|
||||
},
|
||||
};
|
||||
|
||||
pub async fn show_reference_tooltip(
|
||||
State(state): State<AppState>,
|
||||
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
|
||||
Path(reference_id): Path<String>,
|
||||
) -> Result<impl IntoResponse, HtmlError> {
|
||||
info!("Showing reference");
|
||||
|
||||
let user = match auth.current_user {
|
||||
Some(user) => user,
|
||||
None => return Ok(Redirect::to("/").into_response()),
|
||||
};
|
||||
|
||||
let entity: KnowledgeEntity = get_item(&state.surreal_db_client, &reference_id)
|
||||
.await
|
||||
.map_err(|e| HtmlError::new(AppError::from(e), state.templates.clone()))?
|
||||
.ok_or_else(|| {
|
||||
HtmlError::new(
|
||||
AppError::NotFound("Item was not found".to_string()),
|
||||
state.templates.clone(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if entity.user_id != user.id {
|
||||
return Err(HtmlError::new(
|
||||
AppError::Auth("You dont have access to this entity".to_string()),
|
||||
state.templates.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ReferenceTooltipData {
|
||||
entity: KnowledgeEntity,
|
||||
user: User,
|
||||
}
|
||||
|
||||
let output = render_template(
|
||||
"chat/reference_tooltip.html",
|
||||
ReferenceTooltipData { entity, user },
|
||||
state.templates.clone(),
|
||||
)?;
|
||||
|
||||
Ok(output.into_response())
|
||||
}
|
||||
@@ -17,8 +17,9 @@ use html::{
|
||||
account::{delete_account, set_api_key, show_account_page, update_timezone},
|
||||
admin_panel::{show_admin_panel, toggle_registration_status},
|
||||
chat::{
|
||||
message_response_stream::get_response_stream, new_user_message, show_chat_base,
|
||||
show_existing_chat, show_initialized_chat,
|
||||
message_response_stream::get_response_stream, new_chat_user_message, new_user_message,
|
||||
references::show_reference_tooltip, show_chat_base, show_existing_chat,
|
||||
show_initialized_chat,
|
||||
},
|
||||
content::{patch_text_content, show_content_page, show_text_content_edit_form},
|
||||
documentation::{
|
||||
@@ -67,9 +68,11 @@ pub fn html_routes(app_state: &AppState) -> Router<AppState> {
|
||||
.route("/gdpr/accept", post(accept_gdpr))
|
||||
.route("/gdpr/deny", post(deny_gdpr))
|
||||
.route("/search", get(search_result_handler))
|
||||
.route("/chat", get(show_chat_base).post(show_initialized_chat))
|
||||
.route("/chat", get(show_chat_base).post(new_chat_user_message))
|
||||
.route("/initialized-chat", post(show_initialized_chat))
|
||||
.route("/chat/:id", get(show_existing_chat).post(new_user_message))
|
||||
.route("/chat/response-stream", get(get_response_stream))
|
||||
.route("/knowledge/:id", get(show_reference_tooltip))
|
||||
.route("/signout", get(sign_out_user))
|
||||
.route("/signin", get(show_signin_form).post(authenticate_user))
|
||||
.route(
|
||||
|
||||
@@ -34,4 +34,21 @@ impl Message {
|
||||
references,
|
||||
}
|
||||
}
|
||||
pub fn new_ai_message(
|
||||
conversation_id: String,
|
||||
id: String,
|
||||
content: String,
|
||||
references: Option<Vec<String>>,
|
||||
) -> Self {
|
||||
let now = Utc::now();
|
||||
Self {
|
||||
id,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
role: MessageRole::AI,
|
||||
content,
|
||||
references,
|
||||
conversation_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user