feat(server): refactor handlers, router, state management, and memory tools
This commit is contained in:
1 parent
87ddb01063
commit
a083719cf1
36 files changed
+1899
-597
No files matched your search
+68
-18
@@ -19,6 +19,7 @@ pub struct ProjectStores {
|
||||
pub pr_checklists: Store<Vec<PrChecklistItem>>,
|
||||
pub context_workspaces: Store<Vec<ContextWorkspace>>,
|
||||
pub pinned_files: Store<Vec<PinnedFile>>,
|
||||
pub snapshots: Store<Vec<StateSnapshot>>,
|
||||
}
|
||||
|
||||
pub struct CodeStores {
|
||||
@@ -51,7 +52,7 @@ pub struct MemoryState {
|
||||
pub graph: Store<KnowledgeGraph>,
|
||||
pub search_index: RwLock<MemoryIndex>,
|
||||
pub vector_db: tokio::sync::RwLock<Option<VectorDB>>,
|
||||
|
||||
|
||||
pub project: ProjectStores,
|
||||
pub code: CodeStores,
|
||||
pub env: EnvironmentStores,
|
||||
@@ -59,6 +60,7 @@ pub struct MemoryState {
|
||||
|
||||
pub activity_tx: tokio::sync::broadcast::Sender<String>,
|
||||
pub event_bus_tx: tokio::sync::broadcast::Sender<GenericEvent>,
|
||||
pub ollama: Arc<crate::ollama::OllamaClient>,
|
||||
}
|
||||
|
||||
impl MemoryState {
|
||||
@@ -69,6 +71,7 @@ impl MemoryState {
|
||||
let db = crate::db::init_redb(&base);
|
||||
|
||||
Self {
|
||||
ollama: Arc::new(crate::ollama::OllamaClient::new_from_env()),
|
||||
clipboard_watch_mode: tokio::sync::RwLock::new(false),
|
||||
graph: Store::new("knowledge_graph_master", db.clone()),
|
||||
base_dir: base.clone(),
|
||||
@@ -84,13 +87,14 @@ impl MemoryState {
|
||||
}
|
||||
}),
|
||||
vector_db: tokio::sync::RwLock::new(None),
|
||||
|
||||
|
||||
project: ProjectStores {
|
||||
tasks: Store::new("tasks", db.clone()),
|
||||
milestones: Store::new("milestones", db.clone()),
|
||||
pr_checklists: Store::new("pr_checklists", db.clone()),
|
||||
context_workspaces: Store::new("context_workspaces", db.clone()),
|
||||
pinned_files: Store::new("pinned_files", db.clone()),
|
||||
snapshots: Store::new("state_snapshots", db.clone()),
|
||||
},
|
||||
code: CodeStores {
|
||||
ledger: Store::new("audit_ledger", db.clone()),
|
||||
@@ -211,6 +215,31 @@ impl MemoryState {
|
||||
*w = idx;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn record_activity(&self, category: &str, summary: &str, details: Option<&str>) {
|
||||
let ts = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_secs();
|
||||
|
||||
let activity = ActivityRecord {
|
||||
timestamp: ts,
|
||||
category: category.to_string(),
|
||||
summary: summary.to_string(),
|
||||
details: details.map(|s| s.to_string()),
|
||||
};
|
||||
|
||||
let record = serde_json::to_value(&activity).unwrap_or_default();
|
||||
|
||||
self.telemetry.recent_activities.modify(|activities| {
|
||||
activities.push_front(record.clone());
|
||||
if activities.len() > 100 {
|
||||
activities.pop_back();
|
||||
}
|
||||
});
|
||||
|
||||
let _ = self.activity_tx.send(record.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -276,8 +305,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
use crate::embedding::{generate_embedding_async, generate_embeddings_async, cosine_similarity};
|
||||
use crate::embedding::{cosine_similarity, generate_embedding_async, generate_embeddings_async};
|
||||
|
||||
pub struct UnifiedSearchResult {
|
||||
pub id: String,
|
||||
@@ -296,10 +324,17 @@ impl SearchService {
|
||||
Self { state }
|
||||
}
|
||||
|
||||
pub async fn semantic_search(&self, query: &str, _filter_namespace: Option<&str>, limit: usize) -> crate::error::Result<Vec<UnifiedSearchResult>> {
|
||||
let query_emb = generate_embedding_async(query.to_string()).await.unwrap_or_default();
|
||||
pub async fn semantic_search(
|
||||
&self,
|
||||
query: &str,
|
||||
_filter_namespace: Option<&str>,
|
||||
limit: usize,
|
||||
) -> crate::error::Result<Vec<UnifiedSearchResult>> {
|
||||
let query_emb = generate_embedding_async(query.to_string())
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
let mut results = Vec::new();
|
||||
|
||||
|
||||
let mut vdb_search = false;
|
||||
if let Some(vdb) = &*self.state.vector_db.read().await {
|
||||
vdb_search = true;
|
||||
@@ -315,24 +350,28 @@ impl SearchService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !vdb_search {
|
||||
let mut texts_to_embed = Vec::new();
|
||||
let mut metadata = Vec::new();
|
||||
|
||||
|
||||
let snippets = self.state.code.snippets.read_with(|snips| snips.clone());
|
||||
for snippet in snippets {
|
||||
let combined = format!("{} {} {}", snippet.name, snippet.description, snippet.code);
|
||||
texts_to_embed.push(combined);
|
||||
metadata.push((snippet.name, "snippet".to_string(), snippet.description));
|
||||
}
|
||||
|
||||
|
||||
let sticky = self.state.code.sticky.read_with(|s| s.clone());
|
||||
for note in sticky {
|
||||
texts_to_embed.push(note.content.clone());
|
||||
metadata.push(("StickyNote".to_string(), "sticky".to_string(), note.content.chars().take(200).collect::<String>()));
|
||||
metadata.push((
|
||||
"StickyNote".to_string(),
|
||||
"sticky".to_string(),
|
||||
note.content.chars().take(200).collect::<String>(),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
if let Ok(embeddings) = generate_embeddings_async(texts_to_embed).await {
|
||||
for (emb, meta) in embeddings.into_iter().zip(metadata) {
|
||||
let sim = cosine_similarity(&query_emb, &emb);
|
||||
@@ -345,18 +384,29 @@ impl SearchService {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
results.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap_or(std::cmp::Ordering::Equal));
|
||||
|
||||
results.sort_by(|a, b| {
|
||||
b.score
|
||||
.partial_cmp(&a.score)
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
});
|
||||
results.truncate(limit);
|
||||
}
|
||||
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
pub fn keyword_search(&self, query: &str, filter_namespace: Option<&str>, limit: usize) -> crate::error::Result<Vec<UnifiedSearchResult>> {
|
||||
pub fn keyword_search(
|
||||
&self,
|
||||
query: &str,
|
||||
filter_namespace: Option<&str>,
|
||||
limit: usize,
|
||||
) -> crate::error::Result<Vec<UnifiedSearchResult>> {
|
||||
let idx = self.state.get_search_index();
|
||||
let matches = idx.search(query, filter_namespace).map_err(|e| crate::error::AppError::Internal(e.to_string()))?;
|
||||
|
||||
let matches = idx
|
||||
.search(query, filter_namespace)
|
||||
.map_err(|e| crate::error::AppError::Internal(e.to_string()))?;
|
||||
|
||||
let mut results = Vec::new();
|
||||
for (id, doc_type, title, body, score) in matches.into_iter().take(limit) {
|
||||
results.push(UnifiedSearchResult {
|
||||
|
||||
Reference in new issue
Block a user