diff --git a/src/channels/web/handlers/memory.rs b/src/channels/web/handlers/memory.rs index 59655d51..8e50f25e 100644 --- a/src/channels/web/handlers/memory.rs +++ b/src/channels/web/handlers/memory.rs @@ -159,10 +159,10 @@ pub async fn memory_search_handler( .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; let hits: Vec = results - .iter() + .into_iter() .map(|r| SearchHit { - path: r.document_id.to_string(), - content: r.content.clone(), + path: r.document_path, + content: r.content, score: r.score as f64, }) .collect(); diff --git a/src/db/libsql/workspace.rs b/src/db/libsql/workspace.rs index 31c9da17..0493d277 100644 --- a/src/db/libsql/workspace.rs +++ b/src/db/libsql/workspace.rs @@ -515,7 +515,7 @@ impl WorkspaceStore for LibSqlBackend { let mut rows = conn .query( r#" - SELECT c.id, c.document_id, c.content + SELECT c.id, c.document_id, d.path, c.content FROM memory_chunks_fts fts JOIN memory_chunks c ON c._rowid = fts.rowid JOIN memory_documents d ON d.id = c.document_id @@ -542,7 +542,8 @@ impl WorkspaceStore for LibSqlBackend { results.push(RankedResult { chunk_id: get_text(&row, 0).parse().unwrap_or_default(), document_id: get_text(&row, 1).parse().unwrap_or_default(), - content: get_text(&row, 2), + document_path: get_text(&row, 2), + content: get_text(&row, 3), rank: results.len() as u32 + 1, }); } @@ -563,7 +564,7 @@ impl WorkspaceStore for LibSqlBackend { let mut rows = conn .query( r#" - SELECT c.id, c.document_id, c.content + SELECT c.id, c.document_id, d.path, c.content FROM vector_top_k('idx_memory_chunks_embedding', vector(?1), ?2) AS top_k JOIN memory_chunks c ON c._rowid = top_k.id JOIN memory_documents d ON d.id = c.document_id @@ -587,7 +588,8 @@ impl WorkspaceStore for LibSqlBackend { results.push(RankedResult { chunk_id: get_text(&row, 0).parse().unwrap_or_default(), document_id: get_text(&row, 1).parse().unwrap_or_default(), - content: get_text(&row, 2), + document_path: get_text(&row, 2), + content: get_text(&row, 3), rank: results.len() as u32 + 1, }); } diff --git a/src/tools/builtin/memory.rs b/src/tools/builtin/memory.rs index ac768402..dbb6b20b 100644 --- a/src/tools/builtin/memory.rs +++ b/src/tools/builtin/memory.rs @@ -95,15 +95,17 @@ impl Tool for MemorySearchTool { .await .map_err(|e| ToolError::ExecutionFailed(format!("Search failed: {}", e)))?; + let result_count = results.len(); let output = serde_json::json!({ "query": query, - "results": results.iter().map(|r| serde_json::json!({ + "results": results.into_iter().map(|r| serde_json::json!({ "content": r.content, "score": r.score, + "path": r.document_path, "document_id": r.document_id.to_string(), "is_hybrid_match": r.is_hybrid(), })).collect::>(), - "result_count": results.len(), + "result_count": result_count, }); Ok(ToolOutput::success(output, start.elapsed())) diff --git a/src/workspace/repository.rs b/src/workspace/repository.rs index f9e87219..de8c3169 100644 --- a/src/workspace/repository.rs +++ b/src/workspace/repository.rs @@ -431,7 +431,7 @@ impl Repository { let rows = conn .query( r#" - SELECT c.id as chunk_id, c.document_id, c.content, + SELECT c.id as chunk_id, c.document_id, d.path as document_path, c.content, ts_rank_cd(c.content_tsv, plainto_tsquery('english', $3)) as rank FROM memory_chunks c JOIN memory_documents d ON d.id = c.document_id @@ -453,6 +453,7 @@ impl Repository { .map(|(i, row)| RankedResult { chunk_id: row.get("chunk_id"), document_id: row.get("document_id"), + document_path: row.get("document_path"), content: row.get("content"), rank: (i + 1) as u32, }) @@ -473,7 +474,7 @@ impl Repository { let rows = conn .query( r#" - SELECT c.id as chunk_id, c.document_id, c.content, + SELECT c.id as chunk_id, c.document_id, d.path as document_path, c.content, 1 - (c.embedding <=> $3) as similarity FROM memory_chunks c JOIN memory_documents d ON d.id = c.document_id @@ -495,6 +496,7 @@ impl Repository { .map(|(i, row)| RankedResult { chunk_id: row.get("chunk_id"), document_id: row.get("document_id"), + document_path: row.get("document_path"), content: row.get("content"), rank: (i + 1) as u32, }) diff --git a/src/workspace/search.rs b/src/workspace/search.rs index c9bf058d..d25dda09 100644 --- a/src/workspace/search.rs +++ b/src/workspace/search.rs @@ -81,6 +81,8 @@ impl SearchConfig { pub struct SearchResult { /// Document ID containing this chunk. pub document_id: Uuid, + /// File path of the source document. + pub document_path: String, /// Chunk ID. pub chunk_id: Uuid, /// Chunk content. @@ -115,6 +117,8 @@ impl SearchResult { pub struct RankedResult { pub chunk_id: Uuid, pub document_id: Uuid, + /// File path of the source document. + pub document_path: String, pub content: String, pub rank: u32, // 1-based rank } @@ -143,6 +147,7 @@ pub fn reciprocal_rank_fusion( // Track scores and metadata for each chunk struct ChunkInfo { document_id: Uuid, + document_path: String, content: String, score: f32, fts_rank: Option, @@ -162,6 +167,7 @@ pub fn reciprocal_rank_fusion( }) .or_insert(ChunkInfo { document_id: result.document_id, + document_path: result.document_path, content: result.content, score: rrf_score, fts_rank: Some(result.rank), @@ -180,6 +186,7 @@ pub fn reciprocal_rank_fusion( }) .or_insert(ChunkInfo { document_id: result.document_id, + document_path: result.document_path, content: result.content, score: rrf_score, fts_rank: None, @@ -192,6 +199,7 @@ pub fn reciprocal_rank_fusion( .into_iter() .map(|(chunk_id, info)| SearchResult { document_id: info.document_id, + document_path: info.document_path, chunk_id, content: info.content, score: info.score, @@ -235,6 +243,7 @@ mod tests { RankedResult { chunk_id, document_id: doc_id, + document_path: format!("docs/{}.md", doc_id), content: format!("content for chunk {}", chunk_id), rank, }