feat(chat): integrate News namespace sessions from dashboard
Dashboard article follow-up chats now persist to MongoDB as News namespace sessions, making them visible in the Chat page sidebar under "News Chats". On first follow-up message: creates a News session with the article title/URL, saves the system context message, then persists each user and assistant message. Subsequent messages in the same article reuse the existing session ID. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -62,10 +62,7 @@ impl Database {
|
||||
|
||||
/// Raw BSON document collection for queries that need manual
|
||||
/// `_id` → `String` conversion (avoids `ObjectId` deserialization issues).
|
||||
pub fn raw_collection(
|
||||
&self,
|
||||
name: &str,
|
||||
) -> Collection<mongodb::bson::Document> {
|
||||
pub fn raw_collection(&self, name: &str) -> Collection<mongodb::bson::Document> {
|
||||
self.inner.collection(name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,13 +187,7 @@ pub fn ChatPage() -> Element {
|
||||
match chat_complete(sid.clone(), messages_json).await {
|
||||
Ok(response) => {
|
||||
// Save assistant message
|
||||
match save_chat_message(
|
||||
sid,
|
||||
"assistant".to_string(),
|
||||
response,
|
||||
)
|
||||
.await
|
||||
{
|
||||
match save_chat_message(sid, "assistant".to_string(), response).await {
|
||||
Ok(msg) => {
|
||||
messages.write().push(msg);
|
||||
}
|
||||
@@ -258,4 +252,3 @@ pub fn ChatPage() -> Element {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ use dioxus::prelude::*;
|
||||
use dioxus_sdk::storage::use_persistent;
|
||||
|
||||
use crate::components::{ArticleDetail, DashboardSidebar, NewsCardView, PageHeader};
|
||||
use crate::infrastructure::chat::{create_chat_session, save_chat_message};
|
||||
use crate::infrastructure::llm::FollowUpMessage;
|
||||
use crate::models::NewsCard;
|
||||
|
||||
@@ -50,6 +51,8 @@ pub fn DashboardPage() -> Element {
|
||||
let mut is_chatting = use_signal(|| false);
|
||||
// Stores the article text context for the chat system message
|
||||
let mut article_context = use_signal(String::new);
|
||||
// MongoDB session ID for persisting News chat (created on first follow-up)
|
||||
let mut news_session_id: Signal<Option<String>> = use_signal(|| None);
|
||||
|
||||
// Recent search history, persisted in localStorage (capped at MAX_RECENT_SEARCHES)
|
||||
let mut recent_searches =
|
||||
@@ -310,6 +313,7 @@ pub fn DashboardPage() -> Element {
|
||||
summary.set(None);
|
||||
chat_messages.set(Vec::new());
|
||||
article_context.set(String::new());
|
||||
news_session_id.set(None);
|
||||
|
||||
|
||||
let oll_url = ollama_url.read().clone();
|
||||
@@ -358,6 +362,7 @@ pub fn DashboardPage() -> Element {
|
||||
selected_card.set(None);
|
||||
summary.set(None);
|
||||
chat_messages.set(Vec::new());
|
||||
news_session_id.set(None);
|
||||
},
|
||||
summary: summary.read().clone(),
|
||||
is_summarizing: *is_summarizing.read(),
|
||||
@@ -367,49 +372,110 @@ pub fn DashboardPage() -> Element {
|
||||
let oll_url = ollama_url.read().clone();
|
||||
let mdl = ollama_model.read().clone();
|
||||
let ctx = article_context.read().clone();
|
||||
// Capture article info for News session creation
|
||||
let card_title = selected_card
|
||||
.read()
|
||||
.as_ref()
|
||||
.map(|c| c.title.clone())
|
||||
.unwrap_or_default();
|
||||
let card_url = selected_card
|
||||
.read()
|
||||
.as_ref()
|
||||
.map(|c| c.url.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
// Append user message to chat
|
||||
chat_messages
|
||||
// Append user message to local chat
|
||||
chat_messages.write().push(FollowUpMessage {
|
||||
role: "user".into(),
|
||||
content: question.clone(),
|
||||
});
|
||||
|
||||
// Build full message history for Ollama
|
||||
|
||||
.write()
|
||||
.push(FollowUpMessage {
|
||||
role: "user".into(),
|
||||
content: question,
|
||||
});
|
||||
let msgs = {
|
||||
let history = chat_messages.read();
|
||||
let mut all = vec![
|
||||
FollowUpMessage {
|
||||
role: "system".into(),
|
||||
content: format!(
|
||||
let system_msg = format!(
|
||||
"You are a helpful assistant. The user is reading \
|
||||
a news article. Use the following context to answer \
|
||||
their questions. Do NOT comment on the source, \
|
||||
dates, URLs, or formatting.\n\n{ctx}",
|
||||
),
|
||||
},
|
||||
];
|
||||
);
|
||||
let msgs = {
|
||||
let history = chat_messages.read();
|
||||
let mut all = vec![FollowUpMessage {
|
||||
role: "system".into(),
|
||||
content: system_msg.clone(),
|
||||
}];
|
||||
all.extend(history.iter().cloned());
|
||||
all
|
||||
};
|
||||
|
||||
spawn(async move {
|
||||
is_chatting.set(true);
|
||||
match crate::infrastructure::llm::chat_followup(msgs, oll_url, mdl).await {
|
||||
|
||||
// Create News session on first follow-up message
|
||||
let existing_sid = news_session_id.read().clone();
|
||||
let sid = if let Some(id) = existing_sid {
|
||||
id
|
||||
} else {
|
||||
match create_chat_session(
|
||||
card_title,
|
||||
"News".to_string(),
|
||||
"ollama".to_string(),
|
||||
mdl.clone(),
|
||||
card_url,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(session) => {
|
||||
let id = session.id.clone();
|
||||
news_session_id.set(Some(id.clone()));
|
||||
// Persist system context as first message
|
||||
let _ = save_chat_message(
|
||||
id.clone(),
|
||||
"system".to_string(),
|
||||
system_msg,
|
||||
)
|
||||
.await;
|
||||
id
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to create News session: {e}");
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Persist user message
|
||||
if !sid.is_empty() {
|
||||
let _ = save_chat_message(
|
||||
sid.clone(),
|
||||
"user".to_string(),
|
||||
question,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
match crate::infrastructure::llm::chat_followup(
|
||||
msgs, oll_url, mdl,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(reply) => {
|
||||
chat_messages
|
||||
.write()
|
||||
.push(FollowUpMessage {
|
||||
// Persist assistant message
|
||||
if !sid.is_empty() {
|
||||
let _ = save_chat_message(
|
||||
sid,
|
||||
"assistant".to_string(),
|
||||
reply.clone(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
chat_messages.write().push(FollowUpMessage {
|
||||
role: "assistant".into(),
|
||||
content: reply,
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Chat failed: {e}");
|
||||
chat_messages
|
||||
.write()
|
||||
.push(FollowUpMessage {
|
||||
chat_messages.write().push(FollowUpMessage {
|
||||
role: "assistant".into(),
|
||||
content: format!("Error: {e}"),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user