diff --git a/crates/lsp-ai/src/config.rs b/crates/lsp-ai/src/config.rs index 3827a50..ea9631f 100644 --- a/crates/lsp-ai/src/config.rs +++ b/crates/lsp-ai/src/config.rs @@ -371,7 +371,7 @@ impl Config { } } -// This makes testing much easier. +// For teesting use only #[cfg(test)] impl Config { pub fn default_with_file_store_without_models() -> Self { diff --git a/crates/lsp-ai/src/memory_backends/file_store.rs b/crates/lsp-ai/src/memory_backends/file_store.rs index a8828a6..45abb7e 100644 --- a/crates/lsp-ai/src/memory_backends/file_store.rs +++ b/crates/lsp-ai/src/memory_backends/file_store.rs @@ -126,7 +126,7 @@ impl FileStore { return Ok(false); } // This means it has been opened before - let insert_uri = format!("file://{path}"); + let insert_uri = format!("file:///{path}"); if self.file_map.lock().contains_key(&insert_uri) { return Ok(true); } @@ -453,6 +453,42 @@ impl MemoryBackend for FileStore { } } +// For teesting use only +#[cfg(test)] +impl FileStore { + pub fn default_with_filler_file() -> anyhow::Result { + let config = Config::default_with_file_store_without_models(); + let file_store_config = if let config::ValidMemoryBackend::FileStore(file_store_config) = + config.config.memory.clone() + { + file_store_config + } else { + anyhow::bail!("requires a file_store_config") + }; + let f = FileStore::new(file_store_config, config)?; + + let uri = "file:///filler.py"; + let text = r#"# Multiplies two numbers +def multiply_two_numbers(x, y): + return + +# A singular test +assert multiply_two_numbers(2, 3) == 6 +"#; + let params = lsp_types::DidOpenTextDocumentParams { + text_document: lsp_types::TextDocumentItem { + uri: reqwest::Url::parse(uri).unwrap(), + language_id: "filler".to_string(), + version: 0, + text: text.to_string(), + }, + }; + f.opened_text_document(params)?; + + Ok(f) + } +} + #[cfg(test)] mod tests { use super::*; @@ -476,7 +512,7 @@ mod tests { } fn generate_filler_text_document(uri: Option<&str>, text: Option<&str>) -> TextDocumentItem { - let uri = uri.unwrap_or("file://filler/"); + let uri = uri.unwrap_or("file:///filler/"); let text = text.unwrap_or("Here is the document body"); TextDocumentItem { uri: reqwest::Url::parse(uri).unwrap(), @@ -496,7 +532,7 @@ mod tests { let file = file_store .file_map .lock() - .get("file://filler/") + .get("file:///filler/") .unwrap() .clone(); assert_eq!(file.rope.to_string(), "Here is the document body"); @@ -513,8 +549,8 @@ mod tests { let params = RenameFilesParams { files: vec![FileRename { - old_uri: "file://filler/".to_string(), - new_uri: "file://filler2/".to_string(), + old_uri: "file:///filler/".to_string(), + new_uri: "file:///filler2/".to_string(), }], }; file_store.renamed_files(params)?; @@ -522,7 +558,7 @@ mod tests { let file = file_store .file_map .lock() - .get("file://filler2/") + .get("file:///filler2/") .unwrap() .clone(); assert_eq!(file.rope.to_string(), "Here is the document body"); @@ -563,7 +599,7 @@ mod tests { let file = file_store .file_map .lock() - .get("file://filler/") + .get("file:///filler/") .unwrap() .clone(); assert_eq!(file.rope.to_string(), "Hae is the document body"); @@ -583,7 +619,7 @@ mod tests { let file = file_store .file_map .lock() - .get("file://filler/") + .get("file:///filler/") .unwrap() .clone(); assert_eq!(file.rope.to_string(), "abc"); @@ -693,7 +729,7 @@ The end with a trailing new line // Test multi-file let text_document2 = generate_filler_text_document( - Some("file://filler2"), + Some("file:///filler2"), Some( r#"Document Top2 Here is a more complicated document @@ -781,7 +817,7 @@ The end with a trailing new line let params = AdditionalFileStoreParams { build_tree: true }; let file_store = FileStore::new_with_params(file_store_config, config, params)?; - let uri = "file://filler/test.rs"; + let uri = "file:///filler/test.rs"; let text = r#"#[derive(Debug)] struct Rectangle { width: u32, diff --git a/crates/lsp-ai/src/memory_worker.rs b/crates/lsp-ai/src/memory_worker.rs index bea5f85..1b7a481 100644 --- a/crates/lsp-ai/src/memory_worker.rs +++ b/crates/lsp-ai/src/memory_worker.rs @@ -69,8 +69,7 @@ async fn do_build_prompt( params .tx .send(prompt) - .map_err(|_| anyhow::anyhow!("sending on channel failed"))?; - Ok(()) + .map_err(|_| anyhow::anyhow!("sending on channel failed")) } fn do_task( diff --git a/crates/lsp-ai/src/transformer_worker.rs b/crates/lsp-ai/src/transformer_worker.rs index fcbd831..7766a11 100644 --- a/crates/lsp-ai/src/transformer_worker.rs +++ b/crates/lsp-ai/src/transformer_worker.rs @@ -173,7 +173,7 @@ pub fn run( connection, config, ) { - error!("error in transformer worker: {e}") + error!("error in transformer worker: {e:?}") } } @@ -256,7 +256,7 @@ async fn dispatch_request( { Ok(response) => response, Err(e) => { - error!("generating response: {e}"); + error!("generating response: {e:?}"); Response { id: request.get_id(), result: None, @@ -266,7 +266,7 @@ async fn dispatch_request( }; if let Err(e) = connection.sender.send(Message::Response(response)) { - error!("sending response: {e}"); + error!("sending response: {e:?}"); } } @@ -412,7 +412,103 @@ async fn do_generate( #[cfg(test)] mod tests { use super::*; - use crate::memory_backends::{ContextAndCodePrompt, FIMPrompt}; + use crate::memory_backends::{ + file_store::FileStore, ContextAndCodePrompt, FIMPrompt, MemoryBackend, + }; + use serde_json::json; + use std::{sync::mpsc, thread}; + + #[tokio::test] + async fn test_do_completion() -> anyhow::Result<()> { + let (memory_tx, memory_rx) = mpsc::channel(); + let memory_backend: Box = + Box::new(FileStore::default_with_filler_file()?); + thread::spawn(move || memory_worker::run(memory_backend, memory_rx)); + + let transformer_backend: Box = + config::ValidModel::Ollama(serde_json::from_value( + json!({"model": "deepseek-coder:1.3b-base"}), + )?) + .try_into()?; + let completion_request = CompletionRequest::new( + serde_json::from_value(json!(0))?, + serde_json::from_value(json!({ + "position": {"character":10, "line":2}, + "textDocument": { + "uri": "file:///filler.py" + } + }))?, + ); + let mut config = config::Config::default_with_file_store_without_models(); + config.config.completion = Some(serde_json::from_value(json!({ + "model": "model1", + "parameters": { + "options": { + "temperature": 0 + } + } + }))?); + + let result = do_completion( + &transformer_backend, + memory_tx, + &completion_request, + &config, + ) + .await?; + + assert_eq!( + " x * y", + result.result.clone().unwrap()["items"][0]["textEdit"]["newText"] + .as_str() + .unwrap() + ); + assert_eq!( + " return", + result.result.unwrap()["items"][0]["filterText"] + .as_str() + .unwrap() + ); + + Ok(()) + } + + #[tokio::test] + async fn test_do_generate() -> anyhow::Result<()> { + let (memory_tx, memory_rx) = mpsc::channel(); + let memory_backend: Box = + Box::new(FileStore::default_with_filler_file()?); + thread::spawn(move || memory_worker::run(memory_backend, memory_rx)); + + let transformer_backend: Box = + config::ValidModel::Ollama(serde_json::from_value( + json!({"model": "deepseek-coder:1.3b-base"}), + )?) + .try_into()?; + let generation_request = GenerationRequest::new( + serde_json::from_value(json!(0))?, + serde_json::from_value(json!({ + "position": {"character":10, "line":2}, + "textDocument": { + "uri": "file:///filler.py" + }, + "model": "model1", + "parameters": { + "options": { + "temperature": 0 + } + } + }))?, + ); + let result = do_generate(&transformer_backend, memory_tx, &generation_request).await?; + + assert_eq!( + " x * y", + result.result.unwrap()["generatedText"].as_str().unwrap() + ); + + Ok(()) + } #[test] fn test_post_process_fim() {