// src/mcp/memory.rs use reqwest; use serde::{Deserialize, Serialize}; use serde_json::{self, Value}; use std::fs; use std::path::Path; #[derive(Debug, Serialize, Deserialize)] pub struct MemorySearchRequest { pub query: String, pub limit: usize, } #[derive(Debug, Serialize, Deserialize)] pub struct ChatRequest { pub message: String, pub model: Option, } #[derive(Debug, Serialize, Deserialize)] pub struct ConversationImportRequest { pub conversation_data: Value, } #[derive(Debug, Deserialize)] pub struct ApiResponse { pub success: bool, pub error: Option, #[allow(dead_code)] pub message: Option, pub filepath: Option, pub results: Option>, pub memories: Option>, #[allow(dead_code)] pub count: Option, pub memory: Option, pub response: Option, pub memories_used: Option, pub imported_count: Option, pub total_count: Option, } #[derive(Debug, Deserialize)] pub struct MemoryResult { #[allow(dead_code)] pub filepath: String, pub title: Option, pub summary: Option, pub source: Option, pub import_time: Option, pub message_count: Option, } pub struct MemoryClient { base_url: String, client: reqwest::Client, } impl MemoryClient { pub fn new(base_url: Option) -> Self { let url = base_url.unwrap_or_else(|| "http://127.0.0.1:5000".to_string()); Self { base_url: url, client: reqwest::Client::new(), } } pub async fn import_chatgpt_file(&self, filepath: &str) -> Result> { // ファイルを読み込み let content = fs::read_to_string(filepath)?; let json_data: Value = serde_json::from_str(&content)?; // 配列かどうかチェック match json_data.as_array() { Some(conversations) => { // 複数の会話をインポート let mut imported_count = 0; let total_count = conversations.len(); for conversation in conversations { match self.import_single_conversation(conversation.clone()).await { Ok(response) => { if response.success { imported_count += 1; } } Err(e) => { eprintln!("❌ インポートエラー: {}", e); } } } Ok(ApiResponse { success: true, imported_count: Some(imported_count), total_count: Some(total_count), error: None, message: Some(format!("{}個中{}個の会話をインポートしました", total_count, imported_count)), filepath: None, results: None, memories: None, count: None, memory: None, response: None, memories_used: None, }) } None => { // 単一の会話をインポート self.import_single_conversation(json_data).await } } } async fn import_single_conversation(&self, conversation_data: Value) -> Result> { let request = ConversationImportRequest { conversation_data }; let response = self.client .post(&format!("{}/memory/import/chatgpt", self.base_url)) .json(&request) .send() .await?; let result: ApiResponse = response.json().await?; Ok(result) } pub async fn search_memories(&self, query: &str, limit: usize) -> Result> { let request = MemorySearchRequest { query: query.to_string(), limit, }; let response = self.client .post(&format!("{}/memory/search", self.base_url)) .json(&request) .send() .await?; let result: ApiResponse = response.json().await?; Ok(result) } pub async fn list_memories(&self) -> Result> { let response = self.client .get(&format!("{}/memory/list", self.base_url)) .send() .await?; let result: ApiResponse = response.json().await?; Ok(result) } pub async fn get_memory_detail(&self, filepath: &str) -> Result> { let response = self.client .get(&format!("{}/memory/detail", self.base_url)) .query(&[("filepath", filepath)]) .send() .await?; let result: ApiResponse = response.json().await?; Ok(result) } pub async fn chat_with_memory(&self, message: &str) -> Result> { let request = ChatRequest { message: message.to_string(), model: None, }; let response = self.client .post(&format!("{}/chat", self.base_url)) .json(&request) .send() .await?; let result: ApiResponse = response.json().await?; Ok(result) } pub async fn is_server_running(&self) -> bool { match self.client.get(&self.base_url).send().await { Ok(response) => response.status().is_success(), Err(_) => false, } } } pub async fn handle_import(filepath: &str) -> Result<(), Box> { if !Path::new(filepath).exists() { eprintln!("❌ ファイルが見つかりません: {}", filepath); return Ok(()); } let client = MemoryClient::new(None); // サーバーが起動しているかチェック if !client.is_server_running().await { eprintln!("❌ MCP Serverが起動していません。先に 'aigpt server run' を実行してください。"); return Ok(()); } println!("🔄 ChatGPT会話をインポートしています: {}", filepath); match client.import_chatgpt_file(filepath).await { Ok(response) => { if response.success { if let (Some(imported), Some(total)) = (response.imported_count, response.total_count) { println!("✅ {}個中{}個の会話をインポートしました", total, imported); } else { println!("✅ 会話をインポートしました"); if let Some(path) = response.filepath { println!("📁 保存先: {}", path); } } } else { eprintln!("❌ インポートに失敗: {:?}", response.error); } } Err(e) => { eprintln!("❌ インポートエラー: {}", e); } } Ok(()) } pub async fn handle_search(query: &str, limit: usize) -> Result<(), Box> { let client = MemoryClient::new(None); if !client.is_server_running().await { eprintln!("❌ MCP Serverが起動していません。先に 'aigpt server run' を実行してください。"); return Ok(()); } println!("🔍 記憶を検索しています: {}", query); match client.search_memories(query, limit).await { Ok(response) => { if response.success { if let Some(results) = response.results { println!("📚 {}個の記憶が見つかりました:", results.len()); for memory in results { println!(" • {}", memory.title.unwrap_or_else(|| "タイトルなし".to_string())); if let Some(summary) = memory.summary { println!(" 概要: {}", summary); } if let Some(count) = memory.message_count { println!(" メッセージ数: {}", count); } println!(); } } else { println!("📚 記憶が見つかりませんでした"); } } else { eprintln!("❌ 検索に失敗: {:?}", response.error); } } Err(e) => { eprintln!("❌ 検索エラー: {}", e); } } Ok(()) } pub async fn handle_list() -> Result<(), Box> { let client = MemoryClient::new(None); if !client.is_server_running().await { eprintln!("❌ MCP Serverが起動していません。先に 'aigpt server run' を実行してください。"); return Ok(()); } println!("📋 記憶一覧を取得しています..."); match client.list_memories().await { Ok(response) => { if response.success { if let Some(memories) = response.memories { println!("📚 総記憶数: {}", memories.len()); for memory in memories { println!(" • {}", memory.title.unwrap_or_else(|| "タイトルなし".to_string())); if let Some(source) = memory.source { println!(" ソース: {}", source); } if let Some(count) = memory.message_count { println!(" メッセージ数: {}", count); } if let Some(import_time) = memory.import_time { println!(" インポート時刻: {}", import_time); } println!(); } } else { println!("📚 記憶がありません"); } } else { eprintln!("❌ 一覧取得に失敗: {:?}", response.error); } } Err(e) => { eprintln!("❌ 一覧取得エラー: {}", e); } } Ok(()) } pub async fn handle_detail(filepath: &str) -> Result<(), Box> { let client = MemoryClient::new(None); if !client.is_server_running().await { eprintln!("❌ MCP Serverが起動していません。先に 'aigpt server run' を実行してください。"); return Ok(()); } println!("📄 記憶の詳細を取得しています: {}", filepath); match client.get_memory_detail(filepath).await { Ok(response) => { if response.success { if let Some(memory) = response.memory { if let Some(title) = memory.get("title").and_then(|v| v.as_str()) { println!("タイトル: {}", title); } if let Some(source) = memory.get("source").and_then(|v| v.as_str()) { println!("ソース: {}", source); } if let Some(summary) = memory.get("summary").and_then(|v| v.as_str()) { println!("概要: {}", summary); } if let Some(messages) = memory.get("messages").and_then(|v| v.as_array()) { println!("メッセージ数: {}", messages.len()); println!("\n最近のメッセージ:"); for msg in messages.iter().take(5) { if let (Some(role), Some(content)) = ( msg.get("role").and_then(|v| v.as_str()), msg.get("content").and_then(|v| v.as_str()) ) { let content_preview = if content.len() > 100 { format!("{}...", &content[..100]) } else { content.to_string() }; println!(" {}: {}", role, content_preview); } } } } } else { eprintln!("❌ 詳細取得に失敗: {:?}", response.error); } } Err(e) => { eprintln!("❌ 詳細取得エラー: {}", e); } } Ok(()) } pub async fn handle_chat_with_memory(message: &str) -> Result<(), Box> { let client = MemoryClient::new(None); if !client.is_server_running().await { eprintln!("❌ MCP Serverが起動していません。先に 'aigpt server run' を実行してください。"); return Ok(()); } println!("💬 記憶を活用してチャットしています..."); match client.chat_with_memory(message).await { Ok(response) => { if response.success { if let Some(reply) = response.response { println!("🤖 {}", reply); } if let Some(memories_used) = response.memories_used { println!("📚 使用した記憶数: {}", memories_used); } } else { eprintln!("❌ チャットに失敗: {:?}", response.error); } } Err(e) => { eprintln!("❌ チャットエラー: {}", e); } } Ok(()) }