feat: add save_item_from_mcp functionality to core services
Co-authored-by: aider (openai/andrew/openrouter/google/gemini-2.5-pro) <aider@aider.chat>
This commit is contained in:
@@ -1,16 +1,12 @@
|
||||
use anyhow::{Result, anyhow};
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::io::{Write, Read};
|
||||
use std::str::FromStr;
|
||||
use log::{debug, warn};
|
||||
|
||||
use crate::modes::server::common::AppState;
|
||||
use crate::core::async_item_service::AsyncItemService;
|
||||
use crate::core::error::CoreError;
|
||||
use crate::db;
|
||||
use crate::compression_engine::{CompressionType, get_compression_engine};
|
||||
use crate::meta_plugin::{MetaPluginType, get_meta_plugin};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ToolError {
|
||||
@@ -41,93 +37,50 @@ impl KeepTools {
|
||||
|
||||
pub async fn save_item(&self, args: Option<Value>) -> Result<String, ToolError> {
|
||||
let args = args.ok_or_else(|| ToolError::InvalidArguments("Missing arguments".to_string()))?;
|
||||
|
||||
let content = args.get("content")
|
||||
|
||||
let content = args
|
||||
.get("content")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| ToolError::InvalidArguments("Missing 'content' field".to_string()))?;
|
||||
|
||||
let tags: Vec<String> = args.get("tags")
|
||||
|
||||
let tags: Vec<String> = args
|
||||
.get("tags")
|
||||
.and_then(|v| v.as_array())
|
||||
.map(|arr| arr.iter().filter_map(|v| v.as_str().map(|s| s.to_string())).collect())
|
||||
.map(|arr| {
|
||||
arr.iter()
|
||||
.filter_map(|v| v.as_str().map(|s| s.to_string()))
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let metadata: HashMap<String, String> = args.get("metadata")
|
||||
|
||||
let metadata: HashMap<String, String> = args
|
||||
.get("metadata")
|
||||
.and_then(|v| v.as_object())
|
||||
.map(|obj| obj.iter().filter_map(|(k, v)| {
|
||||
v.as_str().map(|s| (k.clone(), s.to_string()))
|
||||
}).collect())
|
||||
.map(|obj| {
|
||||
obj.iter()
|
||||
.filter_map(|(k, v)| v.as_str().map(|s| (k.clone(), s.to_string())))
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
debug!("MCP: Saving item with {} bytes, {} tags, {} metadata entries",
|
||||
content.len(), tags.len(), metadata.len());
|
||||
debug!(
|
||||
"MCP: Saving item with {} bytes, {} tags, {} metadata entries",
|
||||
content.len(),
|
||||
tags.len(),
|
||||
metadata.len()
|
||||
);
|
||||
|
||||
let service = AsyncItemService::new(self.state.data_dir.clone(), self.state.db.clone());
|
||||
let item_with_meta = service
|
||||
.save_item_from_mcp(content.as_bytes().to_vec(), tags, metadata)
|
||||
.await
|
||||
.map_err(|e| ToolError::Other(anyhow!(e)))?;
|
||||
|
||||
let item_id = item_with_meta
|
||||
.item
|
||||
.id
|
||||
.ok_or_else(|| anyhow!("Failed to get item ID"))?;
|
||||
|
||||
let mut conn = self.state.db.lock().await;
|
||||
|
||||
// Create new item
|
||||
let item = db::create_item(&mut *conn, CompressionType::LZ4)?;
|
||||
let item_id = item.id.ok_or_else(|| anyhow!("Failed to get item ID"))?;
|
||||
|
||||
// Save content to file
|
||||
let mut item_path = self.state.data_dir.clone();
|
||||
item_path.push(item_id.to_string());
|
||||
|
||||
let compression_engine = get_compression_engine(CompressionType::LZ4)?;
|
||||
let mut writer = compression_engine.create(item_path)?;
|
||||
writer.write_all(content.as_bytes())?;
|
||||
drop(writer); // Ensure file is closed
|
||||
|
||||
// Add tags
|
||||
for tag in &tags {
|
||||
db::add_tag(&mut *conn, item_id, tag)?;
|
||||
}
|
||||
|
||||
// Add custom metadata
|
||||
for (key, value) in &metadata {
|
||||
db::add_meta(&mut *conn, item_id, key, value)?;
|
||||
}
|
||||
|
||||
// Run metadata plugins
|
||||
let meta_plugins = vec![
|
||||
MetaPluginType::FileMime,
|
||||
MetaPluginType::FileEncoding,
|
||||
MetaPluginType::Binary,
|
||||
MetaPluginType::LineCount,
|
||||
MetaPluginType::WordCount,
|
||||
MetaPluginType::DigestSha256,
|
||||
MetaPluginType::Uid,
|
||||
MetaPluginType::User,
|
||||
MetaPluginType::Hostname,
|
||||
];
|
||||
|
||||
for plugin_type in meta_plugins {
|
||||
let plugin_type_clone = plugin_type.clone();
|
||||
let mut plugin = get_meta_plugin(plugin_type);
|
||||
if plugin.is_supported() {
|
||||
if let Err(e) = plugin.initialize(&*conn, item_id) {
|
||||
warn!("Failed to initialize plugin {:?}: {}", plugin_type_clone, e);
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut item_path = self.state.data_dir.clone();
|
||||
item_path.push(item_id.to_string());
|
||||
|
||||
// Process the file content through the plugin
|
||||
let mut item_path = self.state.data_dir.clone();
|
||||
item_path.push(item_id.to_string());
|
||||
|
||||
let compression_engine = get_compression_engine(CompressionType::LZ4)?;
|
||||
let mut reader = compression_engine.open(item_path)?;
|
||||
let mut buffer = Vec::new();
|
||||
reader.read_to_end(&mut buffer)?;
|
||||
|
||||
plugin.update(&buffer, &*conn);
|
||||
|
||||
if let Err(e) = plugin.finalize(&*conn) {
|
||||
warn!("Failed to finalize plugin {:?}: {}", plugin_type_clone, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(format!("Successfully saved item with ID: {}", item_id))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user