huskies: merge 670_refactor_hoist_chat_history_persistence_into_a_shared_module_replaces_658

This commit is contained in:
dave
2026-04-27 20:05:12 +00:00
parent 615e1c7f73
commit 63d5a500de
6 changed files with 208 additions and 136 deletions
+3 -33
View File
@@ -1,40 +1,21 @@
//! Discord conversation history persistence.
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex as TokioMutex;
use crate::chat::history::{load_chat_history, save_chat_history};
use crate::chat::transport::matrix::RoomConversation;
use crate::slog;
/// Per-channel conversation history, keyed by channel ID.
pub type DiscordConversationHistory = Arc<TokioMutex<HashMap<String, RoomConversation>>>;
/// On-disk format for persisted Discord conversation history.
#[derive(Serialize, Deserialize)]
struct PersistedDiscordHistory {
channels: HashMap<String, RoomConversation>,
}
/// Path to the persisted Discord conversation history file.
const DISCORD_HISTORY_FILE: &str = ".huskies/discord_history.json";
/// Load Discord conversation history from disk.
pub fn load_discord_history(project_root: &std::path::Path) -> HashMap<String, RoomConversation> {
let path = project_root.join(DISCORD_HISTORY_FILE);
let data = match std::fs::read_to_string(&path) {
Ok(d) => d,
Err(_) => return HashMap::new(),
};
let persisted: PersistedDiscordHistory = match serde_json::from_str(&data) {
Ok(p) => p,
Err(e) => {
slog!("[discord] Failed to parse history file: {e}");
return HashMap::new();
}
};
persisted.channels
load_chat_history(project_root, DISCORD_HISTORY_FILE, "discord")
}
/// Save Discord conversation history to disk.
@@ -42,18 +23,7 @@ pub(super) fn save_discord_history(
project_root: &std::path::Path,
history: &HashMap<String, RoomConversation>,
) {
let persisted = PersistedDiscordHistory {
channels: history.clone(),
};
let path = project_root.join(DISCORD_HISTORY_FILE);
match serde_json::to_string_pretty(&persisted) {
Ok(json) => {
if let Err(e) = std::fs::write(&path, json) {
slog!("[discord] Failed to write history file: {e}");
}
}
Err(e) => slog!("[discord] Failed to serialise history: {e}"),
}
save_chat_history(project_root, DISCORD_HISTORY_FILE, "discord", history);
}
// ── Tests ───────────────────────────────────────────────────────────────
@@ -1,5 +1,5 @@
//! Matrix conversation history — per-room message history for LLM context.
use crate::slog;
use crate::chat::history::{load_chat_history, save_chat_history};
use matrix_sdk::ruma::OwnedRoomId;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
@@ -44,32 +44,13 @@ pub struct RoomConversation {
/// event-handler tasks without blocking the sync loop.
pub type ConversationHistory = Arc<TokioMutex<HashMap<OwnedRoomId, RoomConversation>>>;
/// On-disk format for persisted conversation history. Room IDs are stored as
/// strings because `OwnedRoomId` does not implement `Serialize` as a map key.
#[derive(Serialize, Deserialize)]
pub(super) struct PersistedHistory {
pub rooms: HashMap<String, RoomConversation>,
}
/// Path to the persisted conversation history file relative to project root.
pub(super) const HISTORY_FILE: &str = ".huskies/matrix_history.json";
/// Load conversation history from disk, returning an empty map on any error.
pub fn load_history(project_root: &std::path::Path) -> HashMap<OwnedRoomId, RoomConversation> {
let path = project_root.join(HISTORY_FILE);
let data = match std::fs::read_to_string(&path) {
Ok(d) => d,
Err(_) => return HashMap::new(),
};
let persisted: PersistedHistory = match serde_json::from_str(&data) {
Ok(p) => p,
Err(e) => {
slog!("[matrix-bot] Failed to parse history file: {e}");
return HashMap::new();
}
};
persisted
.rooms
let string_map = load_chat_history(project_root, HISTORY_FILE, "matrix-bot");
string_map
.into_iter()
.filter_map(|(k, v)| k.parse::<OwnedRoomId>().ok().map(|room_id| (room_id, v)))
.collect()
@@ -80,21 +61,11 @@ pub fn save_history(
project_root: &std::path::Path,
history: &HashMap<OwnedRoomId, RoomConversation>,
) {
let persisted = PersistedHistory {
rooms: history
.iter()
.map(|(k, v)| (k.to_string(), v.clone()))
.collect(),
};
let path = project_root.join(HISTORY_FILE);
match serde_json::to_string_pretty(&persisted) {
Ok(json) => {
if let Err(e) = std::fs::write(&path, json) {
slog!("[matrix-bot] Failed to write history file: {e}");
}
}
Err(e) => slog!("[matrix-bot] Failed to serialise history: {e}"),
}
let string_map: HashMap<String, RoomConversation> = history
.iter()
.map(|(k, v)| (k.to_string(), v.clone()))
.collect();
save_chat_history(project_root, HISTORY_FILE, "matrix-bot", &string_map);
}
// ---------------------------------------------------------------------------
+3 -33
View File
@@ -1,40 +1,21 @@
//! Slack conversation history persistence.
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex as TokioMutex;
use crate::chat::history::{load_chat_history, save_chat_history};
use crate::chat::transport::matrix::RoomConversation;
use crate::slog;
/// Per-channel conversation history, keyed by channel ID.
pub type SlackConversationHistory = Arc<TokioMutex<HashMap<String, RoomConversation>>>;
/// On-disk format for persisted Slack conversation history.
#[derive(Serialize, Deserialize)]
struct PersistedSlackHistory {
channels: HashMap<String, RoomConversation>,
}
/// Path to the persisted Slack conversation history file.
const SLACK_HISTORY_FILE: &str = ".huskies/slack_history.json";
/// Load Slack conversation history from disk.
pub fn load_slack_history(project_root: &std::path::Path) -> HashMap<String, RoomConversation> {
let path = project_root.join(SLACK_HISTORY_FILE);
let data = match std::fs::read_to_string(&path) {
Ok(d) => d,
Err(_) => return HashMap::new(),
};
let persisted: PersistedSlackHistory = match serde_json::from_str(&data) {
Ok(p) => p,
Err(e) => {
slog!("[slack] Failed to parse history file: {e}");
return HashMap::new();
}
};
persisted.channels
load_chat_history(project_root, SLACK_HISTORY_FILE, "slack")
}
/// Save Slack conversation history to disk.
@@ -42,18 +23,7 @@ pub(super) fn save_slack_history(
project_root: &std::path::Path,
history: &HashMap<String, RoomConversation>,
) {
let persisted = PersistedSlackHistory {
channels: history.clone(),
};
let path = project_root.join(SLACK_HISTORY_FILE);
match serde_json::to_string_pretty(&persisted) {
Ok(json) => {
if let Err(e) = std::fs::write(&path, json) {
slog!("[slack] Failed to write history file: {e}");
}
}
Err(e) => slog!("[slack] Failed to serialise history: {e}"),
}
save_chat_history(project_root, SLACK_HISTORY_FILE, "slack", history);
}
// ── Tests ───────────────────────────────────────────────────────────────
+3 -33
View File
@@ -1,11 +1,10 @@
//! WhatsApp conversation history — per-number message history and messaging window tracking.
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex as TokioMutex;
use crate::chat::history::{load_chat_history, save_chat_history};
use crate::chat::transport::matrix::RoomConversation;
use crate::slog;
// ── Messaging window tracker ─────────────────────────────────────────────
@@ -73,30 +72,12 @@ impl MessagingWindowTracker {
/// Per-sender conversation history, keyed by phone number.
pub type WhatsAppConversationHistory = Arc<TokioMutex<HashMap<String, RoomConversation>>>;
/// On-disk format for persisted WhatsApp conversation history.
#[derive(Serialize, Deserialize)]
struct PersistedWhatsAppHistory {
senders: HashMap<String, RoomConversation>,
}
/// Path to the persisted WhatsApp conversation history file.
const WHATSAPP_HISTORY_FILE: &str = ".huskies/whatsapp_history.json";
/// Load WhatsApp conversation history from disk.
pub fn load_whatsapp_history(project_root: &std::path::Path) -> HashMap<String, RoomConversation> {
let path = project_root.join(WHATSAPP_HISTORY_FILE);
let data = match std::fs::read_to_string(&path) {
Ok(d) => d,
Err(_) => return HashMap::new(),
};
let persisted: PersistedWhatsAppHistory = match serde_json::from_str(&data) {
Ok(p) => p,
Err(e) => {
slog!("[whatsapp] Failed to parse history file: {e}");
return HashMap::new();
}
};
persisted.senders
load_chat_history(project_root, WHATSAPP_HISTORY_FILE, "whatsapp")
}
/// Save WhatsApp conversation history to disk.
@@ -104,18 +85,7 @@ pub(super) fn save_whatsapp_history(
project_root: &std::path::Path,
history: &HashMap<String, RoomConversation>,
) {
let persisted = PersistedWhatsAppHistory {
senders: history.clone(),
};
let path = project_root.join(WHATSAPP_HISTORY_FILE);
match serde_json::to_string_pretty(&persisted) {
Ok(json) => {
if let Err(e) = std::fs::write(&path, json) {
slog!("[whatsapp] Failed to write history file: {e}");
}
}
Err(e) => slog!("[whatsapp] Failed to serialise history: {e}"),
}
save_chat_history(project_root, WHATSAPP_HISTORY_FILE, "whatsapp", history);
}
// ── Tests ───────────────────────────────────────────────────────────────