From 7f2aba93610d832a281f8b9266e848be8405762a Mon Sep 17 00:00:00 2001 From: mitya Date: Wed, 4 Jun 2025 12:38:10 +0200 Subject: [PATCH 01/50] subchat model params and chat id fixes --- .../src/scratchpads/chat_passthrough.rs | 34 ++++++++++++------- refact-agent/engine/src/subchat.rs | 16 +++++---- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/refact-agent/engine/src/scratchpads/chat_passthrough.rs b/refact-agent/engine/src/scratchpads/chat_passthrough.rs index 19993e8a4..b59305589 100644 --- a/refact-agent/engine/src/scratchpads/chat_passthrough.rs +++ b/refact-agent/engine/src/scratchpads/chat_passthrough.rs @@ -1,5 +1,4 @@ use std::sync::Arc; -use indexmap::IndexMap; use serde_json::{json, Value}; use tokenizers::Tokenizer; use tokio::sync::Mutex as AMutex; @@ -109,7 +108,7 @@ impl ScratchpadAbstract for ChatPassthrough { ccx: Arc>, sampling_parameters_to_patch: &mut SamplingParameters, ) -> Result { - let (gcx, n_ctx, should_execute_remotely) = { + let (gcx, mut n_ctx, should_execute_remotely) = { let ccx_locked = ccx.lock().await; (ccx_locked.global_context.clone(), ccx_locked.n_ctx, ccx_locked.should_execute_remotely) }; @@ -178,6 +177,17 @@ impl ScratchpadAbstract for ChatPassthrough { info!("PASSTHROUGH TOOLS NOT SUPPORTED"); } + let caps = { + let gcx_locked = gcx.write().await; + gcx_locked.caps.clone().unwrap() + }; + let model_record_mb = resolve_chat_model(caps, &self.post.model).ok(); + let mut supports_reasoning = None; + if let Some(model_record) = &model_record_mb { + n_ctx = model_record.base.n_ctx.clone(); + supports_reasoning = model_record.supports_reasoning.clone(); + } + let (limited_msgs, compression_strength) = match fix_and_limit_messages_history( &self.t, &messages, @@ -197,25 +207,19 @@ impl ScratchpadAbstract for ChatPassthrough { } // Handle models that support reasoning - let caps = { - let gcx_locked = gcx.write().await; - gcx_locked.caps.clone().unwrap() - }; - let model_record_mb = resolve_chat_model(caps, &self.post.model).ok(); - - let supports_reasoning = model_record_mb.as_ref() - .map_or(false, |m| m.supports_reasoning.is_some()); - - let limited_adapted_msgs = if supports_reasoning { + let limited_adapted_msgs = if let Some(supports_reasoning) = supports_reasoning { let model_record = model_record_mb.clone().unwrap(); _adapt_for_reasoning_models( limited_msgs, sampling_parameters_to_patch, - model_record.supports_reasoning.as_ref().unwrap().clone(), + supports_reasoning, model_record.default_temperature.clone(), model_record.supports_boost_reasoning.clone(), ) } else { + // drop all reasoning parameters in case of non-reasoning model + sampling_parameters_to_patch.reasoning_effort = None; + sampling_parameters_to_patch.thinking = None; limited_msgs }; @@ -278,6 +282,7 @@ fn _adapt_for_reasoning_models( sampling_parameters.reasoning_effort = Some(ReasoningEffort::High); } sampling_parameters.temperature = default_temperature; + sampling_parameters.thinking = None; // NOTE: OpenAI prefer user message over system messages.into_iter().map(|mut msg| { @@ -299,9 +304,12 @@ fn _adapt_for_reasoning_models( "budget_tokens": budget_tokens, })); } + sampling_parameters.reasoning_effort = None; messages }, _ => { + sampling_parameters.reasoning_effort = None; + sampling_parameters.thinking = None; sampling_parameters.temperature = default_temperature.clone(); messages } diff --git a/refact-agent/engine/src/subchat.rs b/refact-agent/engine/src/subchat.rs index 7193accc8..546f2ae63 100644 --- a/refact-agent/engine/src/subchat.rs +++ b/refact-agent/engine/src/subchat.rs @@ -405,13 +405,15 @@ pub async fn subchat( tx_chatid_mb.clone(), ).await?[0].clone(); let last_message = messages.last().unwrap(); - let tool_call_mb = last_message.tool_calls.clone().map(|x|{ - let tool_call = x.get(0).unwrap(); - format!("{}({})", tool_call.function.name, tool_call.function.arguments).to_string() - }).unwrap_or_default(); - let content = format!("πŸ€–:\n{}\n{}\n", &last_message.content.content_text_only(), tool_call_mb); - tx_chatid_mb = Some(format!("{step_n}/{wrap_up_depth}: {content}")); - info!("subchat request {step_n}/{wrap_up_depth}: {content}"); + let mut content = format!("πŸ€–:\n{}", &last_message.content.content_text_only()); + if let Some(tool_calls) = &last_message.tool_calls { + if let Some(tool_call) = tool_calls.get(0) { + content = format!("{}\n{}({})", content, tool_call.function.name, tool_call.function.arguments); + } + } + let tx_chatid = format!("{step_n}/{wrap_up_depth}: {content}"); + info!("subchat request {tx_chatid}"); + tx_chatid_mb = Some(tx_chatid); step_n += 1; } // result => session From 28ae2eb524fbb4f5565197dfe7da645de6157e67 Mon Sep 17 00:00:00 2001 From: Humberto Yusta Date: Tue, 20 May 2025 19:05:30 +0200 Subject: [PATCH 02/50] remove locks of at commands and at command params --- .../src/at_commands/at_ast_definition.rs | 8 ++-- .../src/at_commands/at_ast_reference.rs | 8 ++-- .../engine/src/at_commands/at_commands.rs | 11 +++-- .../engine/src/at_commands/at_file.rs | 8 ++-- .../engine/src/at_commands/at_knowledge.rs | 4 +- .../engine/src/at_commands/at_search.rs | 4 +- .../engine/src/at_commands/at_tree.rs | 4 +- refact-agent/engine/src/at_commands/at_web.rs | 4 +- .../engine/src/at_commands/execute_at.rs | 21 ++++----- .../engine/src/http/routers/v1/at_commands.rs | 43 ++++++++----------- 10 files changed, 52 insertions(+), 63 deletions(-) diff --git a/refact-agent/engine/src/at_commands/at_ast_definition.rs b/refact-agent/engine/src/at_commands/at_ast_definition.rs index a00071e89..9a7caab07 100644 --- a/refact-agent/engine/src/at_commands/at_ast_definition.rs +++ b/refact-agent/engine/src/at_commands/at_ast_definition.rs @@ -36,14 +36,14 @@ impl AtParamSymbolPathQuery { // } pub struct AtAstDefinition { - pub params: Vec>>, + pub params: Vec>, } impl AtAstDefinition { pub fn new() -> Self { AtAstDefinition { params: vec![ - Arc::new(AMutex::new(AtParamSymbolPathQuery::new())) + Box::new(AtParamSymbolPathQuery::new()) ], } } @@ -88,7 +88,7 @@ impl AtParam for AtParamSymbolPathQuery { #[async_trait] impl AtCommand for AtAstDefinition { - fn params(&self) -> &Vec>> { + fn params(&self) -> &Vec> { &self.params } @@ -108,7 +108,7 @@ impl AtCommand for AtAstDefinition { }, }; - correct_at_arg(ccx.clone(), self.params[0].clone(), &mut arg_symbol).await; + correct_at_arg(ccx.clone(), &self.params[0], &mut arg_symbol).await; args.clear(); args.push(arg_symbol.clone()); diff --git a/refact-agent/engine/src/at_commands/at_ast_reference.rs b/refact-agent/engine/src/at_commands/at_ast_reference.rs index f251819a8..44f0d7c49 100644 --- a/refact-agent/engine/src/at_commands/at_ast_reference.rs +++ b/refact-agent/engine/src/at_commands/at_ast_reference.rs @@ -10,14 +10,14 @@ use crate::at_commands::at_ast_definition::AtParamSymbolPathQuery; pub struct AtAstReference { - pub params: Vec>>, + pub params: Vec>, } impl AtAstReference { pub fn new() -> Self { AtAstReference { params: vec![ - Arc::new(AMutex::new(AtParamSymbolPathQuery::new())) + Box::new(AtParamSymbolPathQuery::new()) ], } } @@ -26,7 +26,7 @@ impl AtAstReference { #[async_trait] impl AtCommand for AtAstReference { - fn params(&self) -> &Vec>> { + fn params(&self) -> &Vec> { &self.params } @@ -46,7 +46,7 @@ impl AtCommand for AtAstReference { }, }; - correct_at_arg(ccx.clone(), self.params[0].clone(), &mut arg_symbol).await; + correct_at_arg(ccx.clone(), &self.params[0], &mut arg_symbol).await; args.clear(); args.push(arg_symbol.clone()); diff --git a/refact-agent/engine/src/at_commands/at_commands.rs b/refact-agent/engine/src/at_commands/at_commands.rs index ad9d81c54..c007a5c74 100644 --- a/refact-agent/engine/src/at_commands/at_commands.rs +++ b/refact-agent/engine/src/at_commands/at_commands.rs @@ -32,7 +32,7 @@ pub struct AtCommandsContext { pub current_model: String, pub should_execute_remotely: bool, - pub at_commands: HashMap>>>, // a copy from static constant + pub at_commands: HashMap>, // a copy from static constant pub subchat_tool_parameters: IndexMap, pub postprocess_parameters: PostprocessSettings, @@ -77,7 +77,7 @@ impl AtCommandsContext { #[async_trait] pub trait AtCommand: Send + Sync { - fn params(&self) -> &Vec>>; + fn params(&self) -> &Vec>; // returns (messages_for_postprocessing, text_on_clip) async fn at_execute(&self, ccx: Arc>, cmd: &mut AtCommandMember, args: &mut Vec) -> Result<(Vec, String), String>; fn depends_on(&self) -> Vec { vec![] } // "ast", "vecdb" @@ -90,7 +90,7 @@ pub trait AtParam: Send + Sync { fn param_completion_valid(&self) -> bool {false} } -pub async fn at_commands_dict(gcx: Arc>) -> HashMap>>> { +pub async fn at_commands_dict(gcx: Arc>) -> HashMap> { let at_commands_dict = HashMap::from([ ("@file".to_string(), Arc::new(AMutex::new(Box::new(AtFile::new()) as Box))), // ("@file-search".to_string(), Arc::new(AMutex::new(Box::new(AtFileSearch::new()) as Box))), @@ -116,8 +116,7 @@ pub async fn at_commands_dict(gcx: Arc>) -> HashMap>) -> HashMap>>, + pub params: Vec>, } impl AtFile { pub fn new() -> Self { AtFile { params: vec![ - Arc::new(AMutex::new(AtParamFilePath::new())) + Box::new(AtParamFilePath::new()) ], } } @@ -271,7 +271,7 @@ pub async fn context_file_from_file_path( #[async_trait] impl AtCommand for AtFile { - fn params(&self) -> &Vec>> { + fn params(&self) -> &Vec> { &self.params } @@ -292,7 +292,7 @@ impl AtCommand for AtFile { return Err("Cannot execute @file: no file provided".to_string()); } }; - correct_at_arg(ccx.clone(), self.params[0].clone(), &mut arg0).await; + correct_at_arg(ccx.clone(), &self.params[0], &mut arg0).await; args.clear(); args.push(arg0.clone()); diff --git a/refact-agent/engine/src/at_commands/at_knowledge.rs b/refact-agent/engine/src/at_commands/at_knowledge.rs index fcbc0d82d..551872c09 100644 --- a/refact-agent/engine/src/at_commands/at_knowledge.rs +++ b/refact-agent/engine/src/at_commands/at_knowledge.rs @@ -11,7 +11,7 @@ use crate::memories::memories_search; /// @knowledge-load command - loads knowledge entries by search key or memory ID pub struct AtLoadKnowledge { - params: Vec>>, + params: Vec>, } impl AtLoadKnowledge { @@ -24,7 +24,7 @@ impl AtLoadKnowledge { #[async_trait] impl AtCommand for AtLoadKnowledge { - fn params(&self) -> &Vec>> { + fn params(&self) -> &Vec> { &self.params } diff --git a/refact-agent/engine/src/at_commands/at_search.rs b/refact-agent/engine/src/at_commands/at_search.rs index 6f576b69e..431aba048 100644 --- a/refact-agent/engine/src/at_commands/at_search.rs +++ b/refact-agent/engine/src/at_commands/at_search.rs @@ -20,7 +20,7 @@ pub fn text_on_clip(query: &String, from_tool_call: bool) -> String { pub struct AtSearch { - pub params: Vec>>, + pub params: Vec>, } impl AtSearch { @@ -84,7 +84,7 @@ pub async fn execute_at_search( #[async_trait] impl AtCommand for AtSearch { - fn params(&self) -> &Vec>> { + fn params(&self) -> &Vec> { &self.params } diff --git a/refact-agent/engine/src/at_commands/at_tree.rs b/refact-agent/engine/src/at_commands/at_tree.rs index 4fc187db4..95f19e54f 100644 --- a/refact-agent/engine/src/at_commands/at_tree.rs +++ b/refact-agent/engine/src/at_commands/at_tree.rs @@ -16,7 +16,7 @@ use crate::files_correction::{correct_to_nearest_dir_path, get_project_dirs, pat pub struct AtTree { - pub params: Vec>>, + pub params: Vec>, } impl AtTree { @@ -266,7 +266,7 @@ pub async fn print_files_tree_with_budget( #[async_trait] impl AtCommand for AtTree { - fn params(&self) -> &Vec>> { &self.params } + fn params(&self) -> &Vec> { &self.params } async fn at_execute( &self, diff --git a/refact-agent/engine/src/at_commands/at_web.rs b/refact-agent/engine/src/at_commands/at_web.rs index 9f1bd46ca..768ea1ab9 100644 --- a/refact-agent/engine/src/at_commands/at_web.rs +++ b/refact-agent/engine/src/at_commands/at_web.rs @@ -14,7 +14,7 @@ use crate::call_validation::{ChatMessage, ContextEnum}; pub struct AtWeb { - pub params: Vec>>, + pub params: Vec>, } impl AtWeb { @@ -27,7 +27,7 @@ impl AtWeb { #[async_trait] impl AtCommand for AtWeb { - fn params(&self) -> &Vec>> { + fn params(&self) -> &Vec> { &self.params } diff --git a/refact-agent/engine/src/at_commands/execute_at.rs b/refact-agent/engine/src/at_commands/execute_at.rs index f91b9387d..eb5de78b7 100644 --- a/refact-agent/engine/src/at_commands/execute_at.rs +++ b/refact-agent/engine/src/at_commands/execute_at.rs @@ -205,14 +205,13 @@ pub async fn run_at_commands_remotely( pub async fn correct_at_arg( ccx: Arc>, - param: Arc>, + param: &Box, arg: &mut AtCommandMember, ) { - let param_lock = param.lock().await; - if param_lock.is_value_valid(ccx.clone(), &arg.text).await { + if param.is_value_valid(ccx.clone(), &arg.text).await { return; } - let completion = match param_lock.param_completion(ccx.clone(), &arg.text).await.get(0) { + let completion = match param.param_completion(ccx.clone(), &arg.text).await.get(0) { Some(x) => x.clone(), None => { arg.ok = false; @@ -220,7 +219,7 @@ pub async fn correct_at_arg( return; } }; - if !param_lock.is_value_valid(ccx.clone(), &completion).await { + if !param.is_value_valid(ccx.clone(), &completion).await { arg.ok = false; arg.reason = Some("incorrect argument; completion did not help".to_string()); return; } @@ -231,21 +230,19 @@ pub async fn execute_at_commands_in_query( ccx: Arc>, query: &mut String, ) -> (Vec, Vec) { - let at_commands = { - ccx.lock().await.at_commands.clone() - }; + let context = ccx.lock().await; + let at_commands = &context.at_commands; let at_command_names = at_commands.keys().map(|x|x.clone()).collect::>(); let mut context_enums = vec![]; let mut highlight_members = vec![]; - let mut clips = vec![]; + let mut clips: Vec<(String, usize, usize)> = vec![]; let words = parse_words_from_line(query); for (w_idx, (word, pos1, pos2)) in words.iter().enumerate() { let cmd = match at_commands.get(word) { - Some(c) => c.clone(), + Some(c) => c, None => { continue; } }; - let cmd_lock = cmd.lock().await; let args = words.iter().skip(w_idx + 1).map(|x|x.clone()).collect::>(); let mut cmd_member = AtCommandMember::new("cmd".to_string(), word.clone(), *pos1, *pos2); @@ -256,7 +253,7 @@ pub async fn execute_at_commands_in_query( arg_members.push(AtCommandMember::new("arg".to_string(), text.clone(), pos1, pos2)); } - match cmd_lock.at_execute(ccx.clone(), &mut cmd_member, &mut arg_members).await { + match cmd.at_execute(ccx.clone(), &mut cmd_member, &mut arg_members).await { Ok((res, text_on_clip)) => { context_enums.extend(res); clips.push((text_on_clip, cmd_member.pos1, arg_members.last().map(|x|x.pos2).unwrap_or(cmd_member.pos2))); diff --git a/refact-agent/engine/src/http/routers/v1/at_commands.rs b/refact-agent/engine/src/http/routers/v1/at_commands.rs index 43f083e16..334958b6b 100644 --- a/refact-agent/engine/src/http/routers/v1/at_commands.rs +++ b/refact-agent/engine/src/http/routers/v1/at_commands.rs @@ -103,9 +103,7 @@ pub async fn handle_v1_command_completion( "".to_string(), ).await)); - let at_commands = { - ccx.lock().await.at_commands.clone() - }; + let at_commands = ccx.lock().await.at_commands.clone(); let at_command_names = at_commands.keys().map(|x|x.clone()).collect::>(); let mut completions: Vec = vec![]; @@ -152,7 +150,7 @@ pub async fn handle_v1_command_preview( let post = serde_json::from_slice::(&body_bytes) .map_err(|e| ScratchError::new(StatusCode::UNPROCESSABLE_ENTITY, format!("JSON problem: {}", e)))?; let mut messages = deserialize_messages_from_post(&post.messages)?; - + let last_message = messages.pop(); let mut query = if let Some(last_message) = &last_message { match &last_message.content { @@ -162,7 +160,7 @@ pub async fn handle_v1_command_preview( for element in elements { if element.is_text() { // use last text, but expected to be only one query = element.m_content.clone(); - } + } } query } @@ -246,7 +244,7 @@ pub async fn handle_v1_command_preview( reason: h.reason.unwrap_or_default(), }) } - + let messages_to_count = if let Some(mut last_message) = last_message { match &mut last_message.content { ChatContent::SimpleText(_) => {last_message.content = ChatContent::SimpleText(query.clone());} @@ -263,11 +261,11 @@ pub async fn handle_v1_command_preview( preview.clone() }; let tokens_number = count_tokens(tokenizer_arc.clone(), &messages_to_count).await?; - + Ok(Response::builder() .status(StatusCode::OK) .body(Body::from(serde_json::to_string_pretty( - &json!({"messages": preview, "model": model_rec.base.id, "highlight": highlights, + &json!({"messages": preview, "model": model_rec.base.id, "highlight": highlights, "current_context": tokens_number, "number_context": model_rec.base.n_ctx}) ).unwrap())) .unwrap()) @@ -337,9 +335,7 @@ async fn command_completion( cursor_abs: i64, ) -> (Vec, bool, i64, i64) { // returns ([possible, completions], good_as_it_is) let mut args = args; - let at_commands = { - ccx.lock().await.at_commands.clone() - }; + let at_commands = ccx.lock().await.at_commands.clone(); let at_command_names = at_commands.keys().map(|x|x.clone()).collect::>(); let q_cmd_with_index = args.iter().enumerate().find_map(|(index, x)| { @@ -361,23 +357,22 @@ async fn command_completion( } }; args = args.iter().skip(q_cmd_idx + 1).map(|x|x.clone()).collect::>(); - let cmd_params_cnt = cmd.lock().await.params().len(); + let cmd_params_cnt = cmd.params().len(); args.truncate(cmd_params_cnt); - let can_execute = args.len() == cmd.lock().await.params().len(); + let can_execute = args.len() == cmd.params().len(); - for (arg, param) in args.iter().zip(cmd.lock().await.params()) { - let param_locked = param.lock().await; - let is_valid = param_locked.is_value_valid(ccx.clone(), &arg.value).await; + for (arg, param) in args.iter().zip(cmd.params()) { + let is_valid = param.is_value_valid(ccx.clone(), &arg.value).await; if !is_valid { return if arg.focused { - (param_locked.param_completion(ccx.clone(), &arg.value).await, can_execute, arg.pos1, arg.pos2) + (param.param_completion(ccx.clone(), &arg.value).await, can_execute, arg.pos1, arg.pos2) } else { (vec![], false, -1, -1) } } - if is_valid && arg.focused && param_locked.param_completion_valid() { - return (param_locked.param_completion(ccx.clone(), &arg.value).await, can_execute, arg.pos1, arg.pos2); + if is_valid && arg.focused && param.param_completion_valid() { + return (param.param_completion(ccx.clone(), &arg.value).await, can_execute, arg.pos1, arg.pos2); } } @@ -387,9 +382,9 @@ async fn command_completion( // if command is not focused, and the argument is empty we should make suggestions if !q_cmd.focused { - match cmd.lock().await.params().get(args.len()) { + match cmd.params().get(args.len()) { Some(param) => { - return (param.lock().await.param_completion(ccx.clone(), &"".to_string()).await, false, cursor_abs, cursor_abs); + return (param.param_completion(ccx.clone(), &"".to_string()).await, false, cursor_abs, cursor_abs); }, None => {} } @@ -402,15 +397,13 @@ async fn command_completion_options( ccx: Arc>, q_cmd: &String, ) -> Vec { - let at_commands = { - ccx.lock().await.at_commands.clone() - }; + let at_commands = ccx.lock().await.at_commands.clone(); let at_command_names = at_commands.keys().map(|x|x.clone()).collect::>(); at_command_names .iter() .filter(|command| command.starts_with(q_cmd)) .map(|command| { - (command, jaro_winkler(&command, q_cmd)) + (command.to_string(), jaro_winkler(&command, q_cmd)) }) .sorted_by(|(_, dist1), (_, dist2)| dist1.partial_cmp(dist2).unwrap()) .rev() From 2dda8a7d4bbd08aec157eac90679be59fe4c7930 Mon Sep 17 00:00:00 2001 From: Humberto Yusta Date: Tue, 20 May 2025 19:51:30 +0200 Subject: [PATCH 03/50] remove ast sled db lock --- refact-agent/engine/src/ast/ast_db.rs | 107 ++++++++++-------- .../engine/src/ast/ast_indexer_thread.rs | 4 +- refact-agent/engine/src/ast/ast_structs.rs | 4 +- .../engine/src/at_commands/at_tree.rs | 8 +- .../engine/src/at_commands/execute_at.rs | 3 +- .../engine/src/scratchpads/completon_rag.rs | 2 +- .../engine/src/tools/tool_ast_definition.rs | 2 +- 7 files changed, 69 insertions(+), 61 deletions(-) diff --git a/refact-agent/engine/src/ast/ast_db.rs b/refact-agent/engine/src/ast/ast_db.rs index 74164ae47..a90de3f05 100644 --- a/refact-agent/engine/src/ast/ast_db.rs +++ b/refact-agent/engine/src/ast/ast_db.rs @@ -71,7 +71,7 @@ macro_rules! debug_print { const CACHE_CAPACITY_BYTES: u64 = 256 * 1024 * 1024; // 256M cache -pub async fn ast_index_init(ast_permanent: String, ast_max_files: usize, want_perf_report: bool) -> Arc> +pub async fn ast_index_init(ast_permanent: String, ast_max_files: usize, want_perf_report: bool) -> Arc { let mut config = sled::Config::default() .cache_capacity(CACHE_CAPACITY_BYTES) @@ -95,16 +95,16 @@ pub async fn ast_index_init(ast_permanent: String, ast_max_files: usize, want_pe let ast_index = AstDB { sleddb: db, sledbatch: Arc::new(AMutex::new(sled::Batch::default())), - batch_counter: 0, - counters_increase: HashMap::new(), + batch_counter: AMutex::new(0), + counters_increase: AMutex::new(HashMap::new()), ast_max_files, }; - Arc::new(AMutex::new(ast_index)) + Arc::new(ast_index) } -pub async fn fetch_counters(ast_index: Arc>) -> AstCounters +pub async fn fetch_counters(ast_index: Arc) -> AstCounters { - let db = ast_index.lock().await.sleddb.clone(); + let db = ast_index.sleddb.clone(); let counter_defs = db.get(b"counters|defs").unwrap().map(|v| serde_cbor::from_slice::(&v).unwrap()).unwrap_or(0); let counter_usages = db.get(b"counters|usages").unwrap().map(|v| serde_cbor::from_slice::(&v).unwrap()).unwrap_or(0); let counter_docs = db.get(b"counters|docs").unwrap().map(|v| serde_cbor::from_slice::(&v).unwrap()).unwrap_or(0); @@ -128,46 +128,55 @@ fn _increase_counter_commit(db: &sled::Db, counter_key: &[u8], adjustment: i32) } } -async fn _increase_counter(ast_index: Arc>, counter_key: &str, adjustment: i32) { +async fn _increase_counter(ast_index: Arc, counter_key: &str, adjustment: i32) { if adjustment == 0 { return; } - let mut ast_index_locked = ast_index.lock().await; - let counter = ast_index_locked.counters_increase.entry(counter_key.to_string()).or_insert(0); + let mut counters_increase = ast_index.counters_increase.lock().await; + let counter = counters_increase.entry(counter_key.to_string()).or_insert(0); *counter += adjustment; } pub async fn flush_sled_batch( - ast_db: Arc>, + ast_db: Arc, threshold: usize, // if zero, flush everything including counters ) -> Arc> { - let mut ast_index_locked = ast_db.lock().await; - if ast_index_locked.batch_counter >= threshold { - let sleddb = ast_index_locked.sleddb.clone(); - let batch_arc = std::mem::replace(&mut ast_index_locked.sledbatch, Arc::new(AMutex::new(sled::Batch::default()))); - let was_counter = std::mem::replace(&mut ast_index_locked.batch_counter, 0); - let counters_increase = std::mem::replace(&mut ast_index_locked.counters_increase, HashMap::new()); - drop(ast_index_locked); + let mut batch_counter = ast_db.batch_counter.lock().await; + + if *batch_counter >= threshold { + let sleddb = ast_db.sleddb.clone(); + let mut batch = ast_db.sledbatch.lock().await; + let batch_to_apply = std::mem::replace(&mut *batch, sled::Batch::default()); + drop(batch); + + let was_counter = *batch_counter; + *batch_counter = 0; + + let mut counters_increase = ast_db.counters_increase.lock().await; + let counters_to_process = if threshold == 0 { + std::mem::replace(&mut *counters_increase, HashMap::new()) + } else { + HashMap::new() + }; + drop(counters_increase); + if was_counter > 0 { // tracing::info!("flushing {} sled batches", was_counter); - let mut batch = batch_arc.lock().await; - let batch_to_apply = std::mem::replace(&mut *batch, sled::Batch::default()); if let Err(e) = sleddb.apply_batch(batch_to_apply) { tracing::error!("failed to apply batch: {:?}", e); } } - for (counter_key, adjustment) in counters_increase { + for (counter_key, adjustment) in counters_to_process { _increase_counter_commit(&sleddb, counter_key.as_bytes(), adjustment); } - let ast_index_locked2 = ast_db.lock().await; - return ast_index_locked2.sledbatch.clone(); + return ast_db.sledbatch.clone(); } - ast_index_locked.batch_counter += 1; - ast_index_locked.sledbatch.clone() + *batch_counter += 1; + ast_db.sledbatch.clone() } pub async fn doc_add( - ast_index: Arc>, + ast_index: Arc, cpath: &String, text: &String, errors: &mut AstErrorStats, @@ -175,7 +184,7 @@ pub async fn doc_add( { let file_global_path = filesystem_path_to_double_colon_path(cpath); let (defs, language) = parse_anything_and_add_file_path(&cpath, text, errors)?; // errors mostly "no such parser" here - let db = ast_index.lock().await.sleddb.clone(); + let db = ast_index.sleddb.clone(); let batch_arc = flush_sled_batch(ast_index.clone(), 1000).await; let mut batch = batch_arc.lock().await; let mut added_defs: i32 = 0; @@ -230,11 +239,11 @@ pub async fn doc_add( Ok((defs.into_iter().map(Arc::new).collect(), language)) } -pub async fn doc_remove(ast_index: Arc>, cpath: &String) +pub async fn doc_remove(ast_index: Arc, cpath: &String) { let file_global_path = filesystem_path_to_double_colon_path(cpath); let d_prefix = format!("d|{}::", file_global_path.join("::")); - let db = ast_index.lock().await.sleddb.clone(); + let db = ast_index.sleddb.clone(); let batch_arc = flush_sled_batch(ast_index.clone(), 1000).await; let mut batch = batch_arc.lock().await; let mut iter = db.scan_prefix(d_prefix); @@ -293,9 +302,9 @@ pub async fn doc_remove(ast_index: Arc>, cpath: &String) _increase_counter(ast_index.clone(), "counters|usages", -deleted_usages).await; } -pub async fn doc_defs(ast_index: Arc>, cpath: &String) -> Vec> +pub async fn doc_defs(ast_index: Arc, cpath: &String) -> Vec> { - let db = ast_index.lock().await.sleddb.clone(); + let db = ast_index.sleddb.clone(); doc_def_internal(db, cpath) } @@ -313,10 +322,10 @@ pub fn doc_def_internal(db: Arc, cpath: &String) -> Vec> defs } -pub async fn doc_usages(ast_index: Arc>, cpath: &String) -> Vec<(usize, String)> +pub async fn doc_usages(ast_index: Arc, cpath: &String) -> Vec<(usize, String)> { let definitions = doc_defs(ast_index.clone(), cpath).await; - let db = ast_index.lock().await.sleddb.clone(); + let db = ast_index.sleddb.clone(); let mut usages = Vec::new(); // Simple usages @@ -349,9 +358,9 @@ pub struct ConnectUsageContext { pub t0: Instant, } -pub async fn connect_usages(ast_index: Arc>, ucx: &mut ConnectUsageContext) -> bool +pub async fn connect_usages(ast_index: Arc, ucx: &mut ConnectUsageContext) -> bool { - let db = ast_index.lock().await.sleddb.clone(); + let db = ast_index.sleddb.clone(); let mut iter = db.scan_prefix("resolve-todo|").take(1); if let Some(Ok((todo_key, todo_value))) = iter.next() { @@ -384,10 +393,10 @@ pub async fn connect_usages(ast_index: Arc>, ucx: &mut ConnectUsag false } -pub async fn connect_usages_look_if_full_reset_needed(ast_index: Arc>) -> ConnectUsageContext +pub async fn connect_usages_look_if_full_reset_needed(ast_index: Arc) -> ConnectUsageContext { flush_sled_batch(ast_index.clone(), 0).await; - let db = ast_index.lock().await.sleddb.clone(); + let db = ast_index.sleddb.clone(); let class_hierarchy_key = b"class-hierarchy|"; let existing_hierarchy: IndexMap> = match db.get(class_hierarchy_key) { Ok(Some(value)) => serde_cbor::from_slice(&value).unwrap_or_default(), @@ -647,10 +656,10 @@ async fn _derived_from(db: &sled::Db) -> IndexMap> all_derived_from } -pub async fn usages(ast_index: Arc>, full_official_path: String, limit_n: usize) -> Vec<(Arc, usize)> +pub async fn usages(ast_index: Arc, full_official_path: String, limit_n: usize) -> Vec<(Arc, usize)> { // The best way to get full_official_path is to call definitions() first - let db = ast_index.lock().await.sleddb.clone(); + let db = ast_index.sleddb.clone(); let mut usages = Vec::new(); let u_prefix1 = format!("u|{} ", full_official_path); // this one has space let u_prefix2 = format!("u|{}", full_official_path); @@ -680,9 +689,9 @@ pub async fn usages(ast_index: Arc>, full_official_path: String, l usages } -pub async fn definitions(ast_index: Arc>, double_colon_path: &str) -> Vec> +pub async fn definitions(ast_index: Arc, double_colon_path: &str) -> Vec> { - let db = ast_index.lock().await.sleddb.clone(); + let db = ast_index.sleddb.clone(); let c_prefix1 = format!("c|{} ", double_colon_path); // has space let c_prefix2 = format!("c|{}", double_colon_path); let mut path_groups: HashMap> = HashMap::new(); @@ -720,7 +729,7 @@ pub async fn definitions(ast_index: Arc>, double_colon_path: &str) } #[allow(dead_code)] -pub async fn type_hierarchy(ast_index: Arc>, language: String, subtree_of: String) -> String +pub async fn type_hierarchy(ast_index: Arc, language: String, subtree_of: String) -> String { // Data example: // classes/cppπŸ”ŽAnimal ⚑ alt_testsuite::cpp_goat_library::Goat πŸ‘‰ "cppπŸ”ŽGoat" @@ -740,7 +749,7 @@ pub async fn type_hierarchy(ast_index: Arc>, language: String, sub // CosmicJustice // CosmicGoat // - let db = ast_index.lock().await.sleddb.clone(); + let db = ast_index.sleddb.clone(); let t_prefix = format!("classes|{}", language); let mut iter = db.scan_prefix(&t_prefix); let mut hierarchy_map: IndexMap> = IndexMap::new(); @@ -784,8 +793,8 @@ pub async fn type_hierarchy(ast_index: Arc>, language: String, sub result } -pub async fn definition_paths_fuzzy(ast_index: Arc>, pattern: &str, top_n: usize, max_candidates_to_consider: usize) -> Vec { - let db = ast_index.lock().await.sleddb.clone(); +pub async fn definition_paths_fuzzy(ast_index: Arc, pattern: &str, top_n: usize, max_candidates_to_consider: usize) -> Vec { + let db = ast_index.sleddb.clone(); let mut candidates = HashSet::new(); let mut patterns_to_try = Vec::new(); @@ -833,9 +842,9 @@ pub async fn definition_paths_fuzzy(ast_index: Arc>, pattern: &str } #[allow(dead_code)] -pub async fn dump_database(ast_index: Arc>) -> usize +pub async fn dump_database(ast_index: Arc) -> usize { - let db = ast_index.lock().await.sleddb.clone(); + let db = ast_index.sleddb.clone(); println!("\nsled has {} records", db.len()); let iter = db.iter(); for item in iter { @@ -884,7 +893,7 @@ mod tests { } async fn run_ast_db_test( - ast_index: Arc>, + ast_index: Arc, library_file_path: &str, main_file_path: &str, goat_location: &str, @@ -965,9 +974,9 @@ mod tests { assert_eq!(counters.counter_docs, 0); assert_eq!(dblen, 3 + 1); // 3 counters and 1 class hierarchy - let db = ast_index.lock().await.sleddb.clone(); + let db = ast_index.sleddb.clone(); drop(ast_index); - assert!(Arc::strong_count(&db) == 1); + // assert!(Arc::strong_count(&db) == 1); println!("db.flush"); let x = db.flush().unwrap(); println!("db.flush returned {}, drop", x); diff --git a/refact-agent/engine/src/ast/ast_indexer_thread.rs b/refact-agent/engine/src/ast/ast_indexer_thread.rs index 1553072ad..9a5a1fc87 100644 --- a/refact-agent/engine/src/ast/ast_indexer_thread.rs +++ b/refact-agent/engine/src/ast/ast_indexer_thread.rs @@ -13,7 +13,7 @@ use crate::ast::ast_db::{ast_index_init, fetch_counters, doc_add, doc_remove, fl pub struct AstIndexService { - pub ast_index: Arc>, + pub ast_index: Arc, pub ast_status: Arc>, pub ast_sleeping_point: Arc, pub ast_todo: IndexSet, @@ -41,7 +41,7 @@ async fn ast_indexer_thread( ast_service_locked.ast_sleeping_point.clone(), ) }; - let ast_max_files = ast_index.lock().await.ast_max_files; // cannot change + let ast_max_files = ast_index.ast_max_files; // cannot change loop { let (cpath, left_todo_count) = { diff --git a/refact-agent/engine/src/ast/ast_structs.rs b/refact-agent/engine/src/ast/ast_structs.rs index 471438c65..82c76f1d8 100644 --- a/refact-agent/engine/src/ast/ast_structs.rs +++ b/refact-agent/engine/src/ast/ast_structs.rs @@ -60,8 +60,8 @@ impl AstDefinition { pub struct AstDB { pub sleddb: Arc, pub sledbatch: Arc>, - pub batch_counter: usize, - pub counters_increase: HashMap, + pub batch_counter: AMutex, + pub counters_increase: AMutex>, pub ast_max_files: usize, } diff --git a/refact-agent/engine/src/at_commands/at_tree.rs b/refact-agent/engine/src/at_commands/at_tree.rs index 95f19e54f..3fa05f881 100644 --- a/refact-agent/engine/src/at_commands/at_tree.rs +++ b/refact-agent/engine/src/at_commands/at_tree.rs @@ -157,7 +157,7 @@ fn _print_symbols(db: Arc, path: &PathBuf) -> String { async fn _print_files_tree( tree: &TreeNode, - ast_db: Option>>, + ast_db: Option>, maxdepth: usize, ) -> String { fn traverse( @@ -208,7 +208,7 @@ async fn _print_files_tree( let mut result = String::new(); for (name, node) in &tree.children { let db_mb = if let Some(ast) = ast_db.clone() { - Some(ast.lock().await.sleddb.clone()) + Some(ast.sleddb.clone()) } else { None }; @@ -224,7 +224,7 @@ async fn _print_files_tree( async fn _print_files_tree_with_budget( tree: &TreeNode, char_limit: usize, - ast_db: Option>>, + ast_db: Option>, ) -> String { let mut good_enough = String::new(); for maxdepth in 1..20 { @@ -256,7 +256,7 @@ pub async fn print_files_tree_with_budget( match ast_module_option { Some(ast_module) => { crate::ast::ast_indexer_thread::ast_indexer_block_until_finished(ast_module.clone(), 20_000, true).await; - let ast_db: Option>> = Some(ast_module.lock().await.ast_index.clone()); + let ast_db: Option> = Some(ast_module.lock().await.ast_index.clone()); Ok(_print_files_tree_with_budget(tree, char_limit, ast_db.clone()).await) } None => Ok(_print_files_tree_with_budget(tree, char_limit, None).await), diff --git a/refact-agent/engine/src/at_commands/execute_at.rs b/refact-agent/engine/src/at_commands/execute_at.rs index eb5de78b7..98147d378 100644 --- a/refact-agent/engine/src/at_commands/execute_at.rs +++ b/refact-agent/engine/src/at_commands/execute_at.rs @@ -230,8 +230,7 @@ pub async fn execute_at_commands_in_query( ccx: Arc>, query: &mut String, ) -> (Vec, Vec) { - let context = ccx.lock().await; - let at_commands = &context.at_commands; + let at_commands = ccx.lock().await.at_commands.clone(); let at_command_names = at_commands.keys().map(|x|x.clone()).collect::>(); let mut context_enums = vec![]; let mut highlight_members = vec![]; diff --git a/refact-agent/engine/src/scratchpads/completon_rag.rs b/refact-agent/engine/src/scratchpads/completon_rag.rs index 27706941f..25e012514 100644 --- a/refact-agent/engine/src/scratchpads/completon_rag.rs +++ b/refact-agent/engine/src/scratchpads/completon_rag.rs @@ -77,7 +77,7 @@ async fn _render_context_files( } async fn _cursor_position_to_context_file( - ast_index: Arc>, + ast_index: Arc, cpath: String, cursor_line: i32, context_used: &mut Value, diff --git a/refact-agent/engine/src/tools/tool_ast_definition.rs b/refact-agent/engine/src/tools/tool_ast_definition.rs index 7dc69f837..b9eb74730 100644 --- a/refact-agent/engine/src/tools/tool_ast_definition.rs +++ b/refact-agent/engine/src/tools/tool_ast_definition.rs @@ -135,7 +135,7 @@ impl Tool for ToolAstDefinition { } pub async fn there_are_definitions_with_similar_names_though( - ast_index: Arc>, + ast_index: Arc, symbol: &str, ) -> String { let fuzzy_matches: Vec = crate::ast::ast_db::definition_paths_fuzzy(ast_index.clone(), symbol, 20, 5000) From 327f56279587a886fdea3a8b93b9a46e799afa0e Mon Sep 17 00:00:00 2001 From: Humberto Yusta Date: Mon, 26 May 2025 22:41:20 +0200 Subject: [PATCH 04/50] fix: don't fully postprocess file for preview --- .../engine/src/http/routers/v1/at_commands.rs | 22 +++++++------------ .../engine/src/postprocessing/pp_utils.rs | 19 ++++++++++------ 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/refact-agent/engine/src/http/routers/v1/at_commands.rs b/refact-agent/engine/src/http/routers/v1/at_commands.rs index 334958b6b..8df6b1923 100644 --- a/refact-agent/engine/src/http/routers/v1/at_commands.rs +++ b/refact-agent/engine/src/http/routers/v1/at_commands.rs @@ -14,6 +14,7 @@ use tracing::info; use crate::at_commands::execute_at::run_at_commands_locally; use crate::indexing_utils::wait_for_indexing_if_needed; +use crate::postprocessing::pp_utils::pp_resolve_ctx_file_paths; use crate::tokens; use crate::at_commands::at_commands::AtCommandsContext; use crate::at_commands::execute_at::{execute_at_commands_in_query, parse_words_from_line}; @@ -25,8 +26,6 @@ use crate::global_context::GlobalContext; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum}; use crate::at_commands::at_commands::filter_only_context_file_from_context_tool; use crate::http::routers::v1::chat::deserialize_messages_from_post; -use crate::postprocessing::pp_context_files::postprocess_context_files; -use crate::scratchpads::scratchpad_utils::max_tokens_for_rag_chat; use crate::scratchpads::scratchpad_utils::HasRagResults; @@ -196,8 +195,6 @@ pub async fn handle_v1_command_preview( &mut query ).await; - let rag_n_ctx = max_tokens_for_rag_chat(model_rec.base.n_ctx, 512); // real maxgen may be different -- comes from request - let mut preview: Vec = vec![]; for exec_result in messages_for_postprocessing.iter() { // at commands exec() can produce both role="user" and role="assistant" messages @@ -214,19 +211,16 @@ pub async fn handle_v1_command_preview( pp_settings.max_files_n = crate::http::routers::v1::chat::CHAT_TOP_N; } - let cf = postprocess_context_files( - global_context.clone(), - &mut filter_only_context_file_from_context_tool(&messages_for_postprocessing), - tokenizer_arc.clone(), - rag_n_ctx, - false, - &pp_settings, - ).await; + let mut context_files = filter_only_context_file_from_context_tool(&messages_for_postprocessing); + let ctx_file_paths = pp_resolve_ctx_file_paths(global_context.clone(), &mut context_files).await; + for (context_file, (_, short_path)) in context_files.iter_mut().zip(ctx_file_paths.into_iter()) { + context_file.file_name = short_path; + } - if !cf.is_empty() { + if !context_files.is_empty() { let message = ChatMessage { role: "context_file".to_string(), - content: ChatContent::SimpleText(serde_json::to_string(&cf).unwrap()), + content: ChatContent::SimpleText(serde_json::to_string(&context_files).unwrap()), tool_calls: None, tool_call_id: "".to_string(), ..Default::default() diff --git a/refact-agent/engine/src/postprocessing/pp_utils.rs b/refact-agent/engine/src/postprocessing/pp_utils.rs index 1ecefb7f2..2825f6a97 100644 --- a/refact-agent/engine/src/postprocessing/pp_utils.rs +++ b/refact-agent/engine/src/postprocessing/pp_utils.rs @@ -75,13 +75,12 @@ fn calculate_hash(path: &PathBuf) -> u64 { hasher.finish() } -pub async fn pp_ast_markup_files( +pub async fn pp_resolve_ctx_file_paths( gcx: Arc>, context_file_vec: &mut Vec, -) -> Vec> { +) -> Vec<(String, String)> { let mut unique_cpaths = IndexSet::::new(); for context_file in context_file_vec.iter_mut() { - // Here we assume data came from outside, we can't trust it too much let path_as_presented = context_file.file_name.clone(); let candidates = crate::files_correction::correct_to_nearest_filename(gcx.clone(), &path_as_presented, false, 5).await; let cpath = match candidates.first() { @@ -97,11 +96,17 @@ pub async fn pp_ast_markup_files( let unique_cpaths_vec: Vec = unique_cpaths.into_iter().collect(); let shortified_vec: Vec = shortify_paths(gcx.clone(), &unique_cpaths_vec).await; + unique_cpaths_vec.into_iter().zip(shortified_vec.into_iter()).collect() +} +pub async fn pp_ast_markup_files( + gcx: Arc>, + context_file_vec: &mut Vec, +) -> Vec> { let mut result: Vec> = vec![]; let ast_service = gcx.read().await.ast_service.clone(); - for (cpath, short) in unique_cpaths_vec.iter().zip(shortified_vec.iter()) { - let cpath_pathbuf = PathBuf::from(cpath); + for (cpath, short) in pp_resolve_ctx_file_paths(gcx.clone(), context_file_vec).await { + let cpath_pathbuf = PathBuf::from(&cpath); let cpath_symmetry_breaker: f32 = (calculate_hash(&cpath_pathbuf) as f32) / (u64::MAX as f32) / 100.0; let mut doc = Document::new(&cpath_pathbuf); let text = match get_file_text_from_memory_or_disk(gcx.clone(), &doc.doc_path).await { @@ -123,9 +128,9 @@ pub async fn pp_ast_markup_files( result.push(Arc::new(PPFile { // doesn't matter what size the output vector is symbols_sorted_by_path_len, file_content: text, - cpath: cpath.clone(), + cpath: cpath, cpath_symmetry_breaker, - shorter_path: short.clone(), + shorter_path: short, })); } From 5eab58411e111709ae134379443c073d7bcbfaf9 Mon Sep 17 00:00:00 2001 From: Humberto Yusta Date: Tue, 27 May 2025 13:11:14 +0200 Subject: [PATCH 05/50] fix: remove binary fields from source file extensions, .db .sqlite .uasset and .mdf --- refact-agent/engine/src/file_filter.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/refact-agent/engine/src/file_filter.rs b/refact-agent/engine/src/file_filter.rs index 017eb42c4..4b75075c8 100644 --- a/refact-agent/engine/src/file_filter.rs +++ b/refact-agent/engine/src/file_filter.rs @@ -10,10 +10,10 @@ pub const SOURCE_FILE_EXTENSIONS: &[&str] = &[ "c", "cpp", "cc", "h", "hpp", "cs", "java", "py", "rb", "go", "rs", "swift", "php", "js", "jsx", "ts", "tsx", "lua", "pl", "r", "sh", "bat", "cmd", "ps1", "m", "kt", "kts", "groovy", "dart", "fs", "fsx", "fsi", "html", "htm", "css", - "scss", "sass", "less", "json", "xml", "yml", "yaml", "md", "sql", "db", "sqlite", - "mdf", "cfg", "conf", "ini", "toml", "dockerfile", "ipynb", "rmd", "xml", "kt", - "xaml", "unity", "gd", "uproject", "uasset", "asm", "s", "tex", "makefile", "mk", - "cmake", "gradle", "liquid" + "scss", "sass", "less", "json", "xml", "yml", "yaml", "md", "sql", "cfg", + "conf", "ini", "toml", "dockerfile", "ipynb", "rmd", "xml", "kt", "xaml", + "unity", "gd", "uproject", "asm", "s", "tex", "makefile", "mk", "cmake", + "gradle", "liquid" ]; pub fn is_valid_file(path: &PathBuf, allow_hidden_folders: bool, ignore_size_thresholds: bool) -> Result<(), Box> { From 3172800668531f9f43f7fa5e2b48173554f7eaa8 Mon Sep 17 00:00:00 2001 From: Humberto Yusta Date: Tue, 27 May 2025 13:53:11 +0200 Subject: [PATCH 06/50] fix: only ask for definitions if file is supported by ast --- refact-agent/engine/src/postprocessing/pp_utils.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/refact-agent/engine/src/postprocessing/pp_utils.rs b/refact-agent/engine/src/postprocessing/pp_utils.rs index 2825f6a97..827d50422 100644 --- a/refact-agent/engine/src/postprocessing/pp_utils.rs +++ b/refact-agent/engine/src/postprocessing/pp_utils.rs @@ -6,6 +6,7 @@ use tracing::{info, warn}; use tokio::sync::RwLock as ARwLock; use std::hash::{Hash, Hasher}; +use crate::ast::treesitter::parsers::get_ast_parser_by_filename; use crate::call_validation::{ContextFile, PostprocessSettings}; use crate::global_context::GlobalContext; use crate::files_in_workspace::{Document, get_file_text_from_memory_or_disk}; @@ -117,11 +118,12 @@ pub async fn pp_ast_markup_files( } }; doc.update_text(&text); - let defs = if let Some(ast) = &ast_service { - let ast_index = ast.lock().await.ast_index.clone(); - crate::ast::ast_db::doc_defs(ast_index.clone(), &doc.doc_path.to_string_lossy().to_string()).await - } else { - vec![] + let defs = match &ast_service { + Some(ast) if get_ast_parser_by_filename(&doc.doc_path).is_ok() => { + let ast_index = ast.lock().await.ast_index.clone(); + crate::ast::ast_db::doc_defs(ast_index, &doc.doc_path.to_string_lossy().to_string()).await + } + _ => vec![], }; let mut symbols_sorted_by_path_len = defs.clone(); symbols_sorted_by_path_len.sort_by_key(|s| s.official_path.len()); From 174e26d01494c290f4a5334b31ea1a0f85b5a4fb Mon Sep 17 00:00:00 2001 From: Humberto Yusta Date: Tue, 27 May 2025 17:30:03 +0200 Subject: [PATCH 07/50] fix: remove unnecesary messages cloning --- .../engine/src/at_commands/execute_at.rs | 35 +++++++++++-------- .../engine/src/http/routers/v1/at_commands.rs | 2 +- .../src/postprocessing/pp_plain_text.rs | 26 +++++++------- .../engine/src/scratchpads/chat_generic.rs | 2 +- .../src/scratchpads/chat_passthrough.rs | 4 +-- .../engine/src/tools/tools_execute.rs | 15 ++++---- 6 files changed, 43 insertions(+), 41 deletions(-) diff --git a/refact-agent/engine/src/at_commands/execute_at.rs b/refact-agent/engine/src/at_commands/execute_at.rs index 98147d378..79430f3f1 100644 --- a/refact-agent/engine/src/at_commands/execute_at.rs +++ b/refact-agent/engine/src/at_commands/execute_at.rs @@ -22,7 +22,7 @@ pub async fn run_at_commands_locally( ccx: Arc>, tokenizer: Option>, maxgen: usize, - original_messages: &Vec, + mut original_messages: Vec, stream_back_to_user: &mut HasRagResults, ) -> (Vec, bool) { let (n_ctx, top_n, is_preview, gcx) = { @@ -56,13 +56,13 @@ pub async fn run_at_commands_locally( // - if there's only 1 user message at the bottom, it receives reserve_for_context tokens for context // - if there are N user messages, they receive reserve_for_context/N tokens each (and there's no taking from one to give to the other) // This is useful to give prefix and suffix of the same file precisely the position necessary for FIM-like operation of a chat model - let mut rebuilt_messages: Vec = original_messages.iter().take(user_msg_starts).map(|m| m.clone()).collect(); - for msg_idx in user_msg_starts..original_messages.len() { - let mut msg = original_messages[msg_idx].clone(); + let messages_after_user_msg = original_messages.split_off(user_msg_starts); + let mut new_messages = original_messages; + for (idx, mut msg) in messages_after_user_msg.into_iter().enumerate() { // todo: make multimodal messages support @commands if let ChatContent::Multimodal(_) = &msg.content { - rebuilt_messages.push(msg.clone()); stream_back_to_user.push_in_json(json!(msg)); + new_messages.push(msg); continue; } let mut content = msg.content.content_text_only(); @@ -71,7 +71,7 @@ pub async fn run_at_commands_locally( let mut context_limit = reserve_for_context / messages_with_at.max(1); context_limit = context_limit.saturating_sub(content_n_tokens); - info!("msg {} user_posted {:?} which is {} tokens, that leaves {} tokens for context of this message", msg_idx, crate::nicer_logs::first_n_chars(&content, 50), content_n_tokens, context_limit); + info!("msg {} user_posted {:?} which is {} tokens, that leaves {} tokens for context of this message", idx + user_msg_starts, crate::nicer_logs::first_n_chars(&content, 50), content_n_tokens, context_limit); let mut messages_exec_output = vec![]; if content.contains("@") { @@ -79,13 +79,19 @@ pub async fn run_at_commands_locally( messages_exec_output.extend(res); } + let mut context_file_pp = if context_limit > MIN_RAG_CONTEXT_LIMIT { + filter_only_context_file_from_context_tool(&messages_exec_output) + } else { + Vec::new() + }; + let mut plain_text_messages = vec![]; - for exec_result in messages_exec_output.iter() { + for exec_result in messages_exec_output.into_iter() { // at commands exec() can produce role "user" "assistant" "diff" "plain_text" if let ContextEnum::ChatMessage(raw_msg) = exec_result { // means not context_file if raw_msg.role != "plain_text" { - rebuilt_messages.push(raw_msg.clone()); stream_back_to_user.push_in_json(json!(raw_msg)); + new_messages.push(raw_msg); } else { plain_text_messages.push(raw_msg); } @@ -95,7 +101,6 @@ pub async fn run_at_commands_locally( // TODO: reduce context_limit by tokens(messages_exec_output) if context_limit > MIN_RAG_CONTEXT_LIMIT { - let mut context_file_pp = filter_only_context_file_from_context_tool(&messages_exec_output); let (tokens_limit_plain, mut tokens_limit_files) = { if context_file_pp.is_empty() { (context_limit, 0) @@ -115,8 +120,8 @@ pub async fn run_at_commands_locally( ).await; for m in pp_plain_text { // OUTPUT: plain text after all custom messages - rebuilt_messages.push(m.clone()); stream_back_to_user.push_in_json(json!(m)); + new_messages.push(m); } tokens_limit_files += non_used_plain; info!("tokens_limit_files {}", tokens_limit_files); @@ -144,8 +149,8 @@ pub async fn run_at_commands_locally( "context_file".to_string(), serde_json::to_string(&json_vec).unwrap_or("".to_string()), ); - rebuilt_messages.push(message.clone()); stream_back_to_user.push_in_json(json!(message)); + new_messages.push(message); } } info!("postprocess_plain_text_messages + postprocess_context_files {:.3}s", t0.elapsed().as_secs_f32()); @@ -154,19 +159,19 @@ pub async fn run_at_commands_locally( if content.trim().len() > 0 { // stream back to the user, with at-commands replaced msg.content = ChatContent::SimpleText(content); - rebuilt_messages.push(msg.clone()); stream_back_to_user.push_in_json(json!(msg)); + new_messages.push(msg); } } - (rebuilt_messages, any_context_produced) + (new_messages, any_context_produced) } pub async fn run_at_commands_remotely( ccx: Arc>, model_id: &str, maxgen: usize, - original_messages: &Vec, + original_messages: Vec, stream_back_to_user: &mut HasRagResults, ) -> Result<(Vec, bool), String> { let (gcx, n_ctx, subchat_tool_parameters, postprocess_parameters, chat_id) = { @@ -181,7 +186,7 @@ pub async fn run_at_commands_remotely( }; let post = CommandExecutePost { - messages: original_messages.clone(), + messages: original_messages, n_ctx, maxgen, subchat_tool_parameters, diff --git a/refact-agent/engine/src/http/routers/v1/at_commands.rs b/refact-agent/engine/src/http/routers/v1/at_commands.rs index 8df6b1923..8c15c8f62 100644 --- a/refact-agent/engine/src/http/routers/v1/at_commands.rs +++ b/refact-agent/engine/src/http/routers/v1/at_commands.rs @@ -297,7 +297,7 @@ pub async fn handle_v1_at_command_execute( let mut has_rag_results = HasRagResults::new(); let (messages, any_context_produced) = run_at_commands_locally( - ccx_arc.clone(), tokenizer.clone(), post.maxgen, &post.messages, &mut has_rag_results).await; + ccx_arc.clone(), tokenizer.clone(), post.maxgen, post.messages, &mut has_rag_results).await; let messages_to_stream_back = has_rag_results.in_json; let undroppable_msg_number = messages.iter().rposition(|msg| msg.role == "user").unwrap_or(0); diff --git a/refact-agent/engine/src/postprocessing/pp_plain_text.rs b/refact-agent/engine/src/postprocessing/pp_plain_text.rs index d4b258244..4108d04bb 100644 --- a/refact-agent/engine/src/postprocessing/pp_plain_text.rs +++ b/refact-agent/engine/src/postprocessing/pp_plain_text.rs @@ -15,7 +15,7 @@ fn limit_text_content( let mut new_text_lines = vec![]; for line in text.lines() { let line_tokens = count_text_tokens_with_fallback(tokenizer.clone(), &line); - if tok_used.clone() + line_tokens > tok_per_m { + if *tok_used + line_tokens > tok_per_m { if new_text_lines.is_empty() { new_text_lines.push("No content: tokens limit reached"); } @@ -29,7 +29,7 @@ fn limit_text_content( } pub async fn postprocess_plain_text( - plain_text_messages: Vec<&ChatMessage>, + plain_text_messages: Vec, tokenizer: Option>, tokens_limit: usize, style: &Option, @@ -37,25 +37,25 @@ pub async fn postprocess_plain_text( if plain_text_messages.is_empty() { return (vec![], tokens_limit); } - let mut messages_sorted = plain_text_messages.clone(); + let mut messages_sorted = plain_text_messages; + let messages_len = messages_sorted.len(); messages_sorted.sort_by(|a, b| a.content.size_estimate(tokenizer.clone(), style).cmp(&b.content.size_estimate(tokenizer.clone(), style))); let mut tok_used_global = 0; - let mut tok_per_m = tokens_limit / messages_sorted.len(); + let mut tok_per_m = tokens_limit / messages_len; let mut new_messages = vec![]; - for (idx, msg) in messages_sorted.iter().cloned().enumerate() { + for (idx, mut msg) in messages_sorted.into_iter().enumerate() { let mut tok_used = 0; - let mut m_cloned = msg.clone(); - - m_cloned.content = match &msg.content { + + msg.content = match msg.content { ChatContent::SimpleText(text) => { - let new_content = limit_text_content(tokenizer.clone(), text, &mut tok_used, tok_per_m); + let new_content = limit_text_content(tokenizer.clone(), &text, &mut tok_used, tok_per_m); ChatContent::SimpleText(new_content) }, ChatContent::Multimodal(elements) => { let mut new_content = vec![]; - + for element in elements { if element.is_text() { let mut el_cloned = element.clone(); @@ -79,13 +79,13 @@ pub async fn postprocess_plain_text( } }; - if idx != messages_sorted.len() - 1 { + if idx != messages_len - 1 { // distributing non-used rest of tokens among the others - tok_per_m += (tok_per_m - tok_used) / (messages_sorted.len() - idx - 1); + tok_per_m += (tok_per_m - tok_used) / (messages_len - idx - 1); } tok_used_global += tok_used; - new_messages.push(m_cloned); + new_messages.push(msg); } let tok_unused = tokens_limit.saturating_sub(tok_used_global); diff --git a/refact-agent/engine/src/scratchpads/chat_generic.rs b/refact-agent/engine/src/scratchpads/chat_generic.rs index cf37d3a98..56fc74671 100644 --- a/refact-agent/engine/src/scratchpads/chat_generic.rs +++ b/refact-agent/engine/src/scratchpads/chat_generic.rs @@ -120,7 +120,7 @@ impl ScratchpadAbstract for GenericChatScratchpad { self.messages.clone() }; let (messages, _any_context_produced) = if self.allow_at && !should_execute_remotely { - run_at_commands_locally(ccx.clone(), self.t.tokenizer.clone(), sampling_parameters_to_patch.max_new_tokens, &messages, &mut self.has_rag_results).await + run_at_commands_locally(ccx.clone(), self.t.tokenizer.clone(), sampling_parameters_to_patch.max_new_tokens, messages, &mut self.has_rag_results).await } else { (self.messages.clone(), false) }; diff --git a/refact-agent/engine/src/scratchpads/chat_passthrough.rs b/refact-agent/engine/src/scratchpads/chat_passthrough.rs index b59305589..ae5bcb552 100644 --- a/refact-agent/engine/src/scratchpads/chat_passthrough.rs +++ b/refact-agent/engine/src/scratchpads/chat_passthrough.rs @@ -126,9 +126,9 @@ impl ScratchpadAbstract for ChatPassthrough { self.messages.clone() }; let (mut messages, _any_context_produced) = if self.allow_at && !should_execute_remotely { - run_at_commands_locally(ccx.clone(), self.t.tokenizer.clone(), sampling_parameters_to_patch.max_new_tokens, &messages, &mut self.has_rag_results).await + run_at_commands_locally(ccx.clone(), self.t.tokenizer.clone(), sampling_parameters_to_patch.max_new_tokens, messages, &mut self.has_rag_results).await } else if self.allow_at { - run_at_commands_remotely(ccx.clone(), &self.post.model, sampling_parameters_to_patch.max_new_tokens, &messages, &mut self.has_rag_results).await? + run_at_commands_remotely(ccx.clone(), &self.post.model, sampling_parameters_to_patch.max_new_tokens, messages, &mut self.has_rag_results).await? } else { (messages, false) }; diff --git a/refact-agent/engine/src/tools/tools_execute.rs b/refact-agent/engine/src/tools/tools_execute.rs index 2d24ede50..204611912 100644 --- a/refact-agent/engine/src/tools/tools_execute.rs +++ b/refact-agent/engine/src/tools/tools_execute.rs @@ -299,16 +299,13 @@ async fn pp_run_tools( ccx: Arc>, original_messages: &Vec, any_corrections: bool, - generated_tool: Vec, - generated_other: Vec, + mut generated_tool: Vec, + mut generated_other: Vec, context_files_for_pp: &mut Vec, tokens_for_rag: usize, tokenizer: Option>, style: &Option, ) -> (Vec, Vec) { - let mut generated_tool = generated_tool.to_vec(); - let mut generated_other = generated_other.to_vec(); - let (top_n, correction_only_up_to_step) = { let ccx_locked = ccx.lock().await; (ccx_locked.top_n, ccx_locked.correction_only_up_to_step) @@ -329,15 +326,15 @@ async fn pp_run_tools( info!("run_tools: tokens_for_rag={} tokens_limit_chat_msg={} tokens_limit_files={}", tokens_for_rag, tokens_limit_chat_msg, tokens_limit_files); let (pp_chat_msg, non_used_tokens_for_rag) = postprocess_plain_text( - generated_tool.iter().chain(generated_other.iter()).collect(), + generated_tool.into_iter().chain(generated_other.into_iter()).collect(), tokenizer.clone(), tokens_limit_chat_msg, style, ).await; // re-add potentially truncated messages, role="tool" will still go first - generated_tool.clear(); - generated_other.clear(); + generated_tool = Vec::new(); + generated_other = Vec::new(); for m in pp_chat_msg { if !m.tool_call_id.is_empty() { generated_tool.push(m.clone()); @@ -371,7 +368,7 @@ async fn pp_run_tools( ).await; if !context_file_vec.is_empty() { - let json_vec: Vec<_> = context_file_vec.iter().map(|p| json!(p)).collect(); + let json_vec: Vec<_> = context_file_vec.into_iter().map(|p| json!(p)).collect(); let message = ChatMessage::new( "context_file".to_string(), serde_json::to_string(&json_vec).unwrap() From f7598660ae1635ea56aa9fde5a302bfd9112e8a0 Mon Sep 17 00:00:00 2001 From: Humberto Yusta Date: Tue, 27 May 2025 20:35:30 +0200 Subject: [PATCH 08/50] update tree-sitter --- refact-agent/engine/Cargo.toml | 14 +++++------ refact-agent/engine/src/ast/parse_python.rs | 3 +-- .../engine/src/ast/treesitter/language_id.rs | 25 +++++++------------ .../engine/src/ast/treesitter/parsers/cpp.rs | 3 +-- .../engine/src/ast/treesitter/parsers/java.rs | 3 +-- .../engine/src/ast/treesitter/parsers/js.rs | 9 +++---- .../src/ast/treesitter/parsers/python.rs | 3 +-- .../engine/src/ast/treesitter/parsers/rust.rs | 7 +++--- .../engine/src/ast/treesitter/parsers/ts.rs | 5 ++-- 9 files changed, 29 insertions(+), 43 deletions(-) diff --git a/refact-agent/engine/Cargo.toml b/refact-agent/engine/Cargo.toml index 416dd3b09..5b64d2246 100644 --- a/refact-agent/engine/Cargo.toml +++ b/refact-agent/engine/Cargo.toml @@ -86,13 +86,13 @@ tower-lsp = "0.20" tracing = "0.1" tracing-appender = "0.2.3" tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] } -tree-sitter = "0.22" -tree-sitter-cpp = "0.22" -tree-sitter-java = "0.21" -tree-sitter-javascript = "0.21" -tree-sitter-python = "0.21" -tree-sitter-rust = "0.21" -tree-sitter-typescript = "0.21" +tree-sitter = "0.25" +tree-sitter-cpp = "0.23" +tree-sitter-java = "0.23" +tree-sitter-javascript = "0.23" +tree-sitter-python = "0.23" +tree-sitter-rust = "0.23" +tree-sitter-typescript = "0.23" typetag = "0.2" url = "2.4.1" uuid = { version = "1", features = ["v4", "serde"] } diff --git a/refact-agent/engine/src/ast/parse_python.rs b/refact-agent/engine/src/ast/parse_python.rs index 723da8c69..e0486e249 100644 --- a/refact-agent/engine/src/ast/parse_python.rs +++ b/refact-agent/engine/src/ast/parse_python.rs @@ -1,6 +1,5 @@ use indexmap::IndexMap; use tree_sitter::{Node, Parser}; -use tree_sitter_python::language; use crate::ast::ast_structs::{AstDefinition, AstUsage, AstErrorStats}; use crate::ast::treesitter::structs::SymbolType; @@ -822,7 +821,7 @@ fn py_body<'a>(cx: &mut ContextPy, node: &Node<'a>, path: &Vec) -> Strin fn py_make_cx(code: &str) -> ContextPy { let mut sitter = Parser::new(); - sitter.set_language(&language()).unwrap(); + sitter.set_language(&tree_sitter_python::LANGUAGE.into()).unwrap(); let cx = ContextPy { ap: ContextAnyParser { sitter, diff --git a/refact-agent/engine/src/ast/treesitter/language_id.rs b/refact-agent/engine/src/ast/treesitter/language_id.rs index 40e3b56bb..716a8fda4 100644 --- a/refact-agent/engine/src/ast/treesitter/language_id.rs +++ b/refact-agent/engine/src/ast/treesitter/language_id.rs @@ -131,22 +131,15 @@ impl From for LanguageId { impl From for LanguageId { fn from(value: Language) -> Self { - if value == tree_sitter_cpp::language() { - Self::Cpp - } else if value == tree_sitter_python::language() { - Self::Python - } else if value == tree_sitter_java::language() { - Self::Java - } else if value == tree_sitter_javascript::language() { - Self::JavaScript - } else if value == tree_sitter_rust::language() { - Self::Rust - } else if value == tree_sitter_typescript::language_typescript() { - Self::TypeScript - } else if value == tree_sitter_typescript::language_tsx() { - Self::TypeScriptReact - } else { - Self::Unknown + match value { + lang if lang == tree_sitter_cpp::LANGUAGE.into() => Self::Cpp, + lang if lang == tree_sitter_python::LANGUAGE.into() => Self::Python, + lang if lang == tree_sitter_java::LANGUAGE.into() => Self::Java, + lang if lang == tree_sitter_javascript::LANGUAGE.into() => Self::JavaScript, + lang if lang == tree_sitter_rust::LANGUAGE.into() => Self::Rust, + lang if lang == tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into() => Self::TypeScript, + lang if lang == tree_sitter_typescript::LANGUAGE_TSX.into() => Self::TypeScriptReact, + _ => Self::Unknown, } } } diff --git a/refact-agent/engine/src/ast/treesitter/parsers/cpp.rs b/refact-agent/engine/src/ast/treesitter/parsers/cpp.rs index 2bfe0c865..848bc1458 100644 --- a/refact-agent/engine/src/ast/treesitter/parsers/cpp.rs +++ b/refact-agent/engine/src/ast/treesitter/parsers/cpp.rs @@ -7,7 +7,6 @@ use parking_lot::RwLock; use similar::DiffableStr; use tree_sitter::{Node, Parser, Range}; -use tree_sitter_cpp::language; use uuid::Uuid; use crate::ast::treesitter::ast_instance_structs::{AstSymbolFields, AstSymbolInstanceArc, ClassFieldDeclaration, CommentDefinition, FunctionArg, FunctionCall, FunctionDeclaration, ImportDeclaration, ImportType, StructDeclaration, TypeDef, VariableDefinition, VariableUsage}; @@ -100,7 +99,7 @@ impl CppParser { pub fn new() -> Result { let mut parser = Parser::new(); parser - .set_language(&language()) + .set_language(&tree_sitter_cpp::LANGUAGE.into()) .map_err(internal_error)?; Ok(CppParser { parser }) } diff --git a/refact-agent/engine/src/ast/treesitter/parsers/java.rs b/refact-agent/engine/src/ast/treesitter/parsers/java.rs index 0a3f38921..42637fa7e 100644 --- a/refact-agent/engine/src/ast/treesitter/parsers/java.rs +++ b/refact-agent/engine/src/ast/treesitter/parsers/java.rs @@ -9,7 +9,6 @@ use itertools::Itertools; use parking_lot::RwLock; use similar::DiffableStr; use tree_sitter::{Node, Parser, Range}; -use tree_sitter_java::language; use uuid::Uuid; use crate::ast::treesitter::ast_instance_structs::{AstSymbolFields, AstSymbolInstanceArc, ClassFieldDeclaration, CommentDefinition, FunctionArg, FunctionCall, FunctionDeclaration, ImportDeclaration, ImportType, StructDeclaration, TypeDef, VariableDefinition, VariableUsage}; @@ -220,7 +219,7 @@ impl JavaParser { pub fn new() -> Result { let mut parser = Parser::new(); parser - .set_language(&language()) + .set_language(&tree_sitter_java::LANGUAGE.into()) .map_err(internal_error)?; Ok(JavaParser { parser }) } diff --git a/refact-agent/engine/src/ast/treesitter/parsers/js.rs b/refact-agent/engine/src/ast/treesitter/parsers/js.rs index 2ebc1edea..a2866ef26 100644 --- a/refact-agent/engine/src/ast/treesitter/parsers/js.rs +++ b/refact-agent/engine/src/ast/treesitter/parsers/js.rs @@ -6,7 +6,6 @@ use parking_lot::RwLock; use similar::DiffableStr; use tree_sitter::{Node, Parser, Range}; -use tree_sitter_javascript::language; use uuid::Uuid; use crate::ast::treesitter::ast_instance_structs::{AstSymbolFields, AstSymbolInstanceArc, ClassFieldDeclaration, CommentDefinition, FunctionArg, FunctionCall, FunctionDeclaration, ImportDeclaration, ImportType, StructDeclaration, TypeDef, VariableDefinition, VariableUsage}; @@ -142,7 +141,7 @@ impl JSParser { pub fn new() -> Result { let mut parser = Parser::new(); parser - .set_language(&language()) + .set_language(&tree_sitter_javascript::LANGUAGE.into()) .map_err(internal_error)?; Ok(Self { parser }) } @@ -226,7 +225,7 @@ impl JSParser { end_point: child.end_position(), }; } - + symbols.push(Arc::new(RwLock::new(Box::new(decl)))); symbols } @@ -651,7 +650,7 @@ impl JSParser { if let Some(first) = def.path_components.first() { if vec!["@", ".", ".."].contains(&first.as_str()) { def.import_type = ImportType::UserModule; - } + } } let mut imports: Vec = vec![]; for i in 0..info.node.child_count() { @@ -748,7 +747,7 @@ impl JSParser { let mut ast_fields = AstSymbolFields::default(); ast_fields.file_path = path.clone(); ast_fields.is_error = false; - ast_fields.language = LanguageId::from(language()); + ast_fields.language = LanguageId::TypeScript; let mut candidates = VecDeque::from(vec![CandidateInfo { ast_fields, diff --git a/refact-agent/engine/src/ast/treesitter/parsers/python.rs b/refact-agent/engine/src/ast/treesitter/parsers/python.rs index a596da72a..75d4f364e 100644 --- a/refact-agent/engine/src/ast/treesitter/parsers/python.rs +++ b/refact-agent/engine/src/ast/treesitter/parsers/python.rs @@ -8,7 +8,6 @@ use itertools::Itertools; use parking_lot::RwLock; use similar::DiffableStr; use tree_sitter::{Node, Parser, Point, Range}; -use tree_sitter_python::language; use uuid::Uuid; use crate::ast::treesitter::ast_instance_structs::{AstSymbolFields, AstSymbolInstanceArc, ClassFieldDeclaration, CommentDefinition, FunctionArg, FunctionCall, FunctionDeclaration, ImportDeclaration, ImportType, StructDeclaration, SymbolInformation, TypeDef, VariableDefinition, VariableUsage}; @@ -211,7 +210,7 @@ impl PythonParser { pub fn new() -> Result { let mut parser = Parser::new(); parser - .set_language(&language()) + .set_language(&tree_sitter_python::LANGUAGE.into()) .map_err(internal_error)?; Ok(PythonParser { parser }) } diff --git a/refact-agent/engine/src/ast/treesitter/parsers/rust.rs b/refact-agent/engine/src/ast/treesitter/parsers/rust.rs index dfbdfaec2..41dc0bfb0 100644 --- a/refact-agent/engine/src/ast/treesitter/parsers/rust.rs +++ b/refact-agent/engine/src/ast/treesitter/parsers/rust.rs @@ -5,7 +5,6 @@ use parking_lot::RwLock; use similar::DiffableStr; use tree_sitter::{Node, Parser, Point, Range}; -use tree_sitter_rust::language; use uuid::Uuid; use crate::ast::treesitter::ast_instance_structs::{AstSymbolInstance, AstSymbolInstanceArc, ClassFieldDeclaration, CommentDefinition, FunctionArg, FunctionCall, FunctionDeclaration, ImportDeclaration, ImportType, StructDeclaration, TypeAlias, TypeDef, VariableDefinition, VariableUsage}; @@ -29,7 +28,7 @@ impl RustParser { pub fn new() -> Result { let mut parser = Parser::new(); parser - .set_language(&language()) + .set_language(&tree_sitter_rust::LANGUAGE.into()) .map_err(internal_error)?; Ok(RustParser { parser }) } @@ -717,7 +716,7 @@ impl RustParser { symbols } - + fn parse_use_declaration(&mut self, parent: &Node, code: &str, path: &PathBuf, parent_guid: &Uuid, is_error: bool) -> Vec { let mut symbols: Vec = vec![]; let argument_node = parent.child_by_field_name("argument").unwrap(); @@ -926,7 +925,7 @@ impl RustParser { } symbols } - + pub fn parse_block(&mut self, parent: &Node, code: &str, path: &PathBuf, parent_guid: &Uuid, is_error: bool) -> Vec { let mut symbols: Vec = vec![]; for i in 0..parent.child_count() { diff --git a/refact-agent/engine/src/ast/treesitter/parsers/ts.rs b/refact-agent/engine/src/ast/treesitter/parsers/ts.rs index c5afa368f..6f29abec0 100644 --- a/refact-agent/engine/src/ast/treesitter/parsers/ts.rs +++ b/refact-agent/engine/src/ast/treesitter/parsers/ts.rs @@ -8,7 +8,6 @@ use parking_lot::RwLock; use similar::DiffableStr; use tree_sitter::{Node, Parser, Range}; -use tree_sitter_typescript::language_typescript as language; use uuid::Uuid; use crate::ast::treesitter::ast_instance_structs::{AstSymbolFields, AstSymbolInstanceArc, ClassFieldDeclaration, CommentDefinition, FunctionArg, FunctionCall, FunctionDeclaration, ImportDeclaration, ImportType, StructDeclaration, TypeDef, VariableDefinition, VariableUsage}; @@ -134,7 +133,7 @@ impl TSParser { pub fn new() -> Result { let mut parser = Parser::new(); parser - .set_language(&language()) + .set_language(&tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()) .map_err(internal_error)?; Ok(Self { parser }) } @@ -244,7 +243,7 @@ impl TSParser { end_point: child.end_position(), }; } - + symbols.push(Arc::new(RwLock::new(Box::new(decl)))); symbols } From d7956a3e4b5ceb7364fdc1bb972382f6fa64323c Mon Sep 17 00:00:00 2001 From: Humberto Yusta Date: Wed, 28 May 2025 11:12:51 +0200 Subject: [PATCH 09/50] add how much did it take to build AST --- refact-agent/engine/src/ast/ast_indexer_thread.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/refact-agent/engine/src/ast/ast_indexer_thread.rs b/refact-agent/engine/src/ast/ast_indexer_thread.rs index 9a5a1fc87..260a08013 100644 --- a/refact-agent/engine/src/ast/ast_indexer_thread.rs +++ b/refact-agent/engine/src/ast/ast_indexer_thread.rs @@ -23,6 +23,7 @@ async fn ast_indexer_thread( gcx_weak: Weak>, ast_service: Arc>, ) { + let t0 = tokio::time::Instant::now(); let mut reported_parse_stats = true; let mut reported_connect_stats = true; let mut stats_parsed_cnt = 0; @@ -254,8 +255,9 @@ async fn ast_indexer_thread( status_locked.astate = "done".to_string(); } ast_sleeping_point.notify_waiters(); - let _ = write!(std::io::stderr(), "AST COMPLETE\n"); - info!("AST COMPLETE"); // you can see stderr sometimes faster vs logs + let msg = format!("AST COMPLETE in {:.2}s", t0.elapsed().as_secs_f32()); + let _ = write!(std::io::stderr(), "{msg}"); + info!("{msg}"); // you can see stderr sometimes faster vs logs reported_connect_stats = true; } From 4bec6ff6e79ff00772f3e83216a85173b6071b73 Mon Sep 17 00:00:00 2001 From: Humberto Yusta Date: Wed, 28 May 2025 20:14:18 +0200 Subject: [PATCH 10/50] migrating to lmdb: step 1 --- refact-agent/engine/Cargo.toml | 2 +- refact-agent/engine/src/ast/ast_db.rs | 467 +++++++++--------- .../engine/src/ast/ast_indexer_thread.rs | 2 +- refact-agent/engine/src/ast/ast_structs.rs | 6 +- 4 files changed, 242 insertions(+), 235 deletions(-) diff --git a/refact-agent/engine/Cargo.toml b/refact-agent/engine/Cargo.toml index 5b64d2246..8144e5c77 100644 --- a/refact-agent/engine/Cargo.toml +++ b/refact-agent/engine/Cargo.toml @@ -34,6 +34,7 @@ git2 = "0.20.2" glob = "0.3.1" hashbrown = "0.15.2" headless_chrome = "1.0.16" +heed = "0.22.0" home = "0.5" html2text = "0.12.5" hyper = { version = "0.14", features = ["server", "stream"] } @@ -69,7 +70,6 @@ shell-words = "1.1.0" shell-escape = "0.1.5" select = "0.6.0" similar = "2.3.0" -sled = { version = "0.34", default-features = false, features = [] } sqlite-vec = { version = "0.1.6" } strip-ansi-escapes = "0.2.1" strsim = "0.11.1" diff --git a/refact-agent/engine/src/ast/ast_db.rs b/refact-agent/engine/src/ast/ast_db.rs index a90de3f05..cb820c9c0 100644 --- a/refact-agent/engine/src/ast/ast_db.rs +++ b/refact-agent/engine/src/ast/ast_db.rs @@ -1,16 +1,19 @@ +use std::path::PathBuf; use std::time::Instant; use std::collections::{HashMap, HashSet}; use std::sync::Arc; +use futures::io::Cursor; +use heed::{RoTxn, RwTxn}; use indexmap::IndexMap; use tokio::sync::Mutex as AMutex; use tokio::task; use serde_cbor; -use sled::Db; use lazy_static::lazy_static; use regex::Regex; use crate::ast::ast_structs::{AstDB, AstDefinition, AstCounters, AstErrorStats}; use crate::ast::ast_parse_anything::{parse_anything_and_add_file_path, filesystem_path_to_double_colon_path}; +use crate::custom_error::MapErrToString; use crate::fuzzy_search::fuzzy_search; // ## How the database works ## @@ -69,32 +72,37 @@ macro_rules! debug_print { }; } -const CACHE_CAPACITY_BYTES: u64 = 256 * 1024 * 1024; // 256M cache - -pub async fn ast_index_init(ast_permanent: String, ast_max_files: usize, want_perf_report: bool) -> Arc +pub async fn ast_index_init(ast_permanent: String, ast_max_files: usize) -> Arc { - let mut config = sled::Config::default() - .cache_capacity(CACHE_CAPACITY_BYTES) - .use_compression(false) - .print_profile_on_drop(want_perf_report) - .mode(sled::Mode::HighThroughput) - .flush_every_ms(Some(5000)); - - if ast_permanent.is_empty() { - config = config.temporary(true).create_new(true); + let db_temp_dir = if ast_permanent.is_empty() { + Some(tempfile::TempDir::new().expect("Failed to create tempdir")) } else { - config = config.path(ast_permanent.clone()); - } + None + }; + let db_path = if let Some(tempdir) = &db_temp_dir { + tempdir.path().to_path_buf() + } else { + PathBuf::from(&ast_permanent) + }; tracing::info!("starting AST db, ast_permanent={:?}", ast_permanent); - let db: Arc = Arc::new(task::spawn_blocking( - move || config.open().unwrap() - ).await.unwrap()); - db.clear().unwrap(); + let db_env: Arc = Arc::new(task::spawn_blocking(move || { + let options = heed::EnvOpenOptions::new(); + unsafe { options.open(db_path).unwrap() } + }).await.unwrap()); + + let db: Arc> = Arc::new(db_env.write_txn().map(|mut txn| { + let db = db_env.create_database(&mut txn, Some("ast")).expect("Failed to create ast db"); + let _ = db.clear(&mut txn); + txn.commit().expect("Failed to commit to lmdb env"); + db + }).expect("Failed to start transaction to create ast db")); + tracing::info!("/starting AST"); let ast_index = AstDB { - sleddb: db, - sledbatch: Arc::new(AMutex::new(sled::Batch::default())), + db_env, + db, + db_temp_dir, batch_counter: AMutex::new(0), counters_increase: AMutex::new(HashMap::new()), ast_max_files, @@ -104,10 +112,19 @@ pub async fn ast_index_init(ast_permanent: String, ast_max_files: usize, want_pe pub async fn fetch_counters(ast_index: Arc) -> AstCounters { - let db = ast_index.sleddb.clone(); - let counter_defs = db.get(b"counters|defs").unwrap().map(|v| serde_cbor::from_slice::(&v).unwrap()).unwrap_or(0); - let counter_usages = db.get(b"counters|usages").unwrap().map(|v| serde_cbor::from_slice::(&v).unwrap()).unwrap_or(0); - let counter_docs = db.get(b"counters|docs").unwrap().map(|v| serde_cbor::from_slice::(&v).unwrap()).unwrap_or(0); + let txn = ast_index.db_env.read_txn().unwrap(); + let counter_defs = ast_index.db.get(&txn, "counters|defs") + .expect("Failed to get counters|defs") + .map(|v| serde_cbor::from_slice::(&v).unwrap()) + .unwrap_or(0); + let counter_usages = ast_index.db.get(&txn, "counters|usages") + .expect("Failed to get counters|usages") + .map(|v| serde_cbor::from_slice::(&v).unwrap()) + .unwrap_or(0); + let counter_docs = ast_index.db.get(&txn, "counters|docs") + .expect("Failed to get counters|docs") + .map(|v| serde_cbor::from_slice::(&v).unwrap()) + .unwrap_or(0); AstCounters { counter_defs, counter_usages, @@ -115,66 +132,19 @@ pub async fn fetch_counters(ast_index: Arc) -> AstCounters } } -fn _increase_counter_commit(db: &sled::Db, counter_key: &[u8], adjustment: i32) { +fn increase_counter<'a>(ast_index: Arc, txn: &mut heed::RwTxn<'a>, counter_key: &str, adjustment: i32) { if adjustment == 0 { return; } - match db.update_and_fetch(counter_key, |counter| { - let counter = counter.map(|v| serde_cbor::from_slice::(&v).unwrap()).unwrap_or(0) + adjustment; - Some(serde_cbor::to_vec(&counter).unwrap()) - }) { - Ok(_) => {}, - Err(e) => tracing::error!("failed to update and fetch counter: {:?}", e), + let new_value = ast_index.db.get(txn, counter_key) + .unwrap_or(None) + .map(|v| serde_cbor::from_slice::(v).unwrap()) + .unwrap_or(0) + adjustment; + if let Err(e) = ast_index.db.put(txn, counter_key, &serde_cbor::to_vec(&new_value).unwrap()) { + tracing::error!("failed to update counter: {:?}", e); } } -async fn _increase_counter(ast_index: Arc, counter_key: &str, adjustment: i32) { - if adjustment == 0 { - return; - } - let mut counters_increase = ast_index.counters_increase.lock().await; - let counter = counters_increase.entry(counter_key.to_string()).or_insert(0); - *counter += adjustment; -} - -pub async fn flush_sled_batch( - ast_db: Arc, - threshold: usize, // if zero, flush everything including counters -) -> Arc> { - let mut batch_counter = ast_db.batch_counter.lock().await; - - if *batch_counter >= threshold { - let sleddb = ast_db.sleddb.clone(); - let mut batch = ast_db.sledbatch.lock().await; - let batch_to_apply = std::mem::replace(&mut *batch, sled::Batch::default()); - drop(batch); - - let was_counter = *batch_counter; - *batch_counter = 0; - - let mut counters_increase = ast_db.counters_increase.lock().await; - let counters_to_process = if threshold == 0 { - std::mem::replace(&mut *counters_increase, HashMap::new()) - } else { - HashMap::new() - }; - drop(counters_increase); - - if was_counter > 0 { - // tracing::info!("flushing {} sled batches", was_counter); - if let Err(e) = sleddb.apply_batch(batch_to_apply) { - tracing::error!("failed to apply batch: {:?}", e); - } - } - for (counter_key, adjustment) in counters_to_process { - _increase_counter_commit(&sleddb, counter_key.as_bytes(), adjustment); - } - return ast_db.sledbatch.clone(); - } - *batch_counter += 1; - ast_db.sledbatch.clone() -} - pub async fn doc_add( ast_index: Arc, cpath: &String, @@ -184,137 +154,167 @@ pub async fn doc_add( { let file_global_path = filesystem_path_to_double_colon_path(cpath); let (defs, language) = parse_anything_and_add_file_path(&cpath, text, errors)?; // errors mostly "no such parser" here - let db = ast_index.sleddb.clone(); - let batch_arc = flush_sled_batch(ast_index.clone(), 1000).await; - let mut batch = batch_arc.lock().await; - let mut added_defs: i32 = 0; - let mut added_usages: i32 = 0; - let mut unresolved_usages: i32 = 0; - for definition in defs.iter() { - assert!(definition.cpath == *cpath); - let serialized = serde_cbor::to_vec(&definition).unwrap(); - let official_path = definition.official_path.join("::"); - let d_key = format!("d|{}", official_path); - debug_print!("writing {}", d_key); - batch.insert(d_key.as_bytes(), serialized); - let mut path_parts: Vec<&str> = definition.official_path.iter().map(|s| s.as_str()).collect(); - while !path_parts.is_empty() { - let c_key = format!("c|{} ⚑ {}", path_parts.join("::"), official_path); - batch.insert(c_key.as_bytes(), b""); - path_parts.remove(0); - } - for usage in &definition.usages { - if !usage.resolved_as.is_empty() { - let u_key = format!("u|{} ⚑ {}", usage.resolved_as, official_path); - batch.insert(u_key.as_bytes(), serde_cbor::to_vec(&usage.uline).unwrap()); - } else if usage.targets_for_guesswork.len() == 1 && !usage.targets_for_guesswork[0].starts_with("?::") { - let homeless_key = format!("homeless|{} ⚑ {}", usage.targets_for_guesswork[0], official_path); - batch.insert(homeless_key.as_bytes(), serde_cbor::to_vec(&usage.uline).unwrap()); - debug_print!(" homeless {}", homeless_key); - continue; - } else { - unresolved_usages += 1; - } - added_usages += 1; - } - // this_is_a_class: cppπŸ”ŽCosmicGoat, derived_from: "cppπŸ”ŽGoat" "cppπŸ”ŽCosmicJustice" - for from in &definition.this_class_derived_from { - let t_key = format!("classes|{} ⚑ {}", from, official_path); - batch.insert(t_key.as_bytes(), definition.this_is_a_class.as_bytes()); - } - added_defs += 1; - } - if unresolved_usages > 0 { - let resolve_todo_key = format!("resolve-todo|{}", file_global_path.join("::")); - batch.insert(resolve_todo_key.as_bytes(), cpath.as_bytes()); - } - let doc_key = format!("doc-cpath|{}", file_global_path.join("::")); - if db.get(doc_key.as_bytes()).unwrap().is_none() { - _increase_counter(ast_index.clone(), "counters|docs", 1).await; - db.insert(doc_key.as_bytes(), cpath.as_bytes()).unwrap(); - } - _increase_counter(ast_index.clone(), "counters|defs", added_defs).await; - _increase_counter(ast_index.clone(), "counters|usages", added_usages).await; - - Ok((defs.into_iter().map(Arc::new).collect(), language)) -} -pub async fn doc_remove(ast_index: Arc, cpath: &String) -{ - let file_global_path = filesystem_path_to_double_colon_path(cpath); - let d_prefix = format!("d|{}::", file_global_path.join("::")); - let db = ast_index.sleddb.clone(); - let batch_arc = flush_sled_batch(ast_index.clone(), 1000).await; - let mut batch = batch_arc.lock().await; - let mut iter = db.scan_prefix(d_prefix); - let mut deleted_defs: i32 = 0; - let mut deleted_usages: i32 = 0; - while let Some(Ok((key, value))) = iter.next() { - let d_key = key.clone(); - if let Ok(definition) = serde_cbor::from_slice::(&value) { - let mut path_parts: Vec<&str> = definition.official_path.iter().map(|s| s.as_str()).collect(); + let result = ast_index.db_env.write_txn().and_then(|mut txn| { + let mut added_defs: i32 = 0; + let mut added_usages: i32 = 0; + let mut unresolved_usages: i32 = 0; + for definition in defs.iter() { + assert!(definition.cpath == *cpath); + let serialized = serde_cbor::to_vec(&definition).unwrap(); let official_path = definition.official_path.join("::"); + let d_key = format!("d|{}", official_path); + debug_print!("writing {}", d_key); + ast_index.db.put(&mut txn, &d_key, &serialized)?; + let mut path_parts: Vec<&str> = definition.official_path.iter().map(|s| s.as_str()).collect(); while !path_parts.is_empty() { let c_key = format!("c|{} ⚑ {}", path_parts.join("::"), official_path); - batch.remove(c_key.as_bytes()); + ast_index.db.put(&mut txn, &c_key, b"")?; path_parts.remove(0); } for usage in &definition.usages { if !usage.resolved_as.is_empty() { let u_key = format!("u|{} ⚑ {}", usage.resolved_as, official_path); - batch.remove(u_key.as_bytes()); + ast_index.db.put(&mut txn, &u_key, &serde_cbor::to_vec(&usage.uline).unwrap())?; } else if usage.targets_for_guesswork.len() == 1 && !usage.targets_for_guesswork[0].starts_with("?::") { let homeless_key = format!("homeless|{} ⚑ {}", usage.targets_for_guesswork[0], official_path); - batch.remove(homeless_key.as_bytes()); + ast_index.db.put(&mut txn, &homeless_key, &serde_cbor::to_vec(&usage.uline).unwrap())?; debug_print!(" homeless {}", homeless_key); continue; + } else { + unresolved_usages += 1; } - deleted_usages += 1; + added_usages += 1; } + // this_is_a_class: cppπŸ”ŽCosmicGoat, derived_from: "cppπŸ”ŽGoat" "cppπŸ”ŽCosmicJustice" for from in &definition.this_class_derived_from { let t_key = format!("classes|{} ⚑ {}", from, official_path); - batch.remove(t_key.as_bytes()); + ast_index.db.put(&mut txn, &t_key, &definition.this_is_a_class.as_bytes())?; } - let cleanup_key = format!("resolve-cleanup|{}", definition.official_path.join("::")); - if let Ok(Some(cleanup_value)) = db.get(cleanup_key.as_bytes()) { - if let Ok(all_saved_ulinks) = serde_cbor::from_slice::>(&cleanup_value) { - for ulink in all_saved_ulinks { - batch.remove(ulink.as_bytes()); + added_defs += 1; + } + if unresolved_usages > 0 { + let resolve_todo_key = format!("resolve-todo|{}", file_global_path.join("::")); + ast_index.db.put(&mut txn, &resolve_todo_key, &cpath.as_bytes())?; + } + let doc_key = format!("doc-cpath|{}", file_global_path.join("::")); + if ast_index.db.get(&txn, &doc_key)?.is_none() { + increase_counter(ast_index.clone(), &mut txn, "counters|docs", 1); + ast_index.db.put(&mut txn, &doc_key, &cpath.as_bytes())?; + } + increase_counter(ast_index.clone(), &mut txn, "counters|defs", added_defs); + increase_counter(ast_index.clone(), &mut txn, "counters|usages", added_usages); + + txn.commit() + }); + + if let Err(e) = result { + tracing::error!("Failed to add document: {:?}", e); + } + + Ok((defs.into_iter().map(Arc::new).collect(), language)) +} + +pub async fn doc_remove(ast_index: Arc, cpath: &String) +{ + let file_global_path = filesystem_path_to_double_colon_path(cpath); + let d_prefix = format!("d|{}::", file_global_path.join("::")); + + let result = ast_index.db_env.write_txn().and_then(|mut txn| { + let mut keys_to_remove = Vec::new(); + let mut deleted_defs = 0; + let mut deleted_usages = 0; + + { + let mut cursor = ast_index.db.prefix_iter(&txn, &d_prefix)?; + while let Some(Ok((key, value))) = cursor.next() { + let d_key = key.clone(); + if let Ok(definition) = serde_cbor::from_slice::(&value) { + let mut path_parts: Vec<&str> = definition.official_path.iter().map(|s| s.as_str()).collect(); + let official_path = definition.official_path.join("::"); + while !path_parts.is_empty() { + let c_key = format!("c|{} ⚑ {}", path_parts.join("::"), official_path); + keys_to_remove.push(c_key); + path_parts.remove(0); } - } else { - tracing::error!("failed to deserialize cleanup_value for key: {}", cleanup_key); + for usage in &definition.usages { + if !usage.resolved_as.is_empty() { + let u_key = format!("u|{} ⚑ {}", usage.resolved_as, official_path); + keys_to_remove.push(u_key); + } else if usage.targets_for_guesswork.len() == 1 && !usage.targets_for_guesswork[0].starts_with("?::") { + let homeless_key = format!("homeless|{} ⚑ {}", usage.targets_for_guesswork[0], official_path); + debug_print!(" homeless {}", homeless_key); + keys_to_remove.push(homeless_key); + continue; + } + deleted_usages += 1; + } + for from in &definition.this_class_derived_from { + let t_key = format!("classes|{} ⚑ {}", from, official_path); + keys_to_remove.push(t_key); + } + let cleanup_key = format!("resolve-cleanup|{}", definition.official_path.join("::")); + if let Ok(Some(cleanup_value)) = ast_index.db.get(&txn, &cleanup_key) { + if let Ok(all_saved_ulinks) = serde_cbor::from_slice::>(&cleanup_value) { + for ulink in all_saved_ulinks { + keys_to_remove.push(ulink); + } + } else { + tracing::error!("failed to deserialize cleanup_value for key: {}", cleanup_key); + } + keys_to_remove.push(cleanup_key); + } + deleted_defs += 1; } - batch.remove(cleanup_key.as_bytes()); + debug_print!("removing {d_key}"); + keys_to_remove.push(d_key.to_string()); } - deleted_defs += 1; } - debug_print!("removing {}", String::from_utf8_lossy(&d_key)); - batch.remove(&d_key); - } - let doc_resolved_key = format!("doc-resolved|{}", file_global_path.join("::")); - batch.remove(doc_resolved_key.as_bytes()); - let doc_key = format!("doc-cpath|{}", file_global_path.join("::")); - if db.get(doc_key.as_bytes()).unwrap().is_some() { - _increase_counter(ast_index.clone(), "counters|docs", -1).await; - db.remove(doc_key.as_bytes()).unwrap(); + let doc_resolved_key = format!("doc-resolved|{}", file_global_path.join("::")); + keys_to_remove.push(doc_resolved_key); + + for key in keys_to_remove { + ast_index.db.delete(&mut txn, &key)?; + } + + let doc_key = format!("doc-cpath|{}", file_global_path.join("::")); + if ast_index.db.get(&txn, &doc_key)?.is_some() { + increase_counter(ast_index.clone(), &mut txn, "counters|docs", -1); + ast_index.db.delete(&mut txn, &doc_key)?; + } + increase_counter(ast_index.clone(), &mut txn, "counters|defs", -deleted_defs); + increase_counter(ast_index.clone(), &mut txn, "counters|usages", -deleted_usages); + + txn.commit() + }); + + if let Err(e) = result { + tracing::error!("Failed to remove document: {:?}", e); } - _increase_counter(ast_index.clone(), "counters|defs", -deleted_defs).await; - _increase_counter(ast_index.clone(), "counters|usages", -deleted_usages).await; } -pub async fn doc_defs(ast_index: Arc, cpath: &String) -> Vec> +pub fn doc_defs(ast_index: Arc, cpath: &String) -> Vec> { - let db = ast_index.sleddb.clone(); - doc_def_internal(db, cpath) + match ast_index.db_env.read_txn() { + Ok(txn) => doc_defs_internal(ast_index.clone(), &txn, cpath), + Err(e) => { + tracing::error!("Failed to open transaction: {:?}", e); + Vec::new() + } + } } -pub fn doc_def_internal(db: Arc, cpath: &String) -> Vec> -{ - let to_search_prefix = filesystem_path_to_double_colon_path(cpath); - let d_prefix = format!("d|{}::", to_search_prefix.join("::")); +pub fn doc_defs_internal<'a>(ast_index: Arc, txn: &RoTxn<'a>, cpath: &String) -> Vec> { + let d_prefix = format!("d|{}::", filesystem_path_to_double_colon_path(cpath).join("::")); let mut defs = Vec::new(); - let mut iter = db.scan_prefix(d_prefix); - while let Some(Ok((_, value))) = iter.next() { + let mut cursor = match ast_index.db.prefix_iter(txn, &d_prefix) { + Ok(cursor) => cursor, + Err(e) => { + tracing::error!("Failed to open prefix iterator: {:?}", e); + return Vec::new(); + }, + }; + while let Some(Ok((_, value))) = cursor.next() { if let Ok(definition) = serde_cbor::from_slice::(&value) { defs.push(Arc::new(definition)); } @@ -322,13 +322,10 @@ pub fn doc_def_internal(db: Arc, cpath: &String) -> Vec> defs } -pub async fn doc_usages(ast_index: Arc, cpath: &String) -> Vec<(usize, String)> -{ - let definitions = doc_defs(ast_index.clone(), cpath).await; - let db = ast_index.sleddb.clone(); +pub async fn doc_usages(ast_index: Arc, cpath: &String) -> Vec<(usize, String)> { + let definitions = doc_defs(ast_index.clone(), cpath); let mut usages = Vec::new(); - // Simple usages for def in definitions { for usage in &def.usages { if !usage.resolved_as.is_empty() { @@ -337,17 +334,20 @@ pub async fn doc_usages(ast_index: Arc, cpath: &String) -> Vec<(usize, St } } - // Scan for usages that needed resolving let file_global_path = filesystem_path_to_double_colon_path(cpath); let doc_resolved_key = format!("doc-resolved|{}", file_global_path.join("::")); - if let Ok(Some(resolved_usages)) = db.get(doc_resolved_key.as_bytes()) { - let resolved_usages_vec = serde_cbor::from_slice::>(&resolved_usages).unwrap(); - usages.extend(resolved_usages_vec); + if let Ok(txn) = ast_index.db_env.read_txn() { + if let Ok(Some(resolved_usages)) = ast_index.db.get(&txn, &doc_resolved_key) { + if let Ok(resolved_usages_vec) = serde_cbor::from_slice::>(&resolved_usages) { + usages.extend(resolved_usages_vec); + } + } } usages } + pub struct ConnectUsageContext { pub derived_from_map: IndexMap>, pub errstats: AstErrorStats, @@ -358,45 +358,48 @@ pub struct ConnectUsageContext { pub t0: Instant, } -pub async fn connect_usages(ast_index: Arc, ucx: &mut ConnectUsageContext) -> bool +pub fn connect_usages(ast_index: Arc, ucx: &mut ConnectUsageContext) -> Result { - let db = ast_index.sleddb.clone(); - let mut iter = db.scan_prefix("resolve-todo|").take(1); + let mut txn = ast_index.db_env.write_txn() + .map_err_with_prefix("Failed to open transaction:")?; + + let (todo_key, todo_value) = { + let mut cursor = ast_index.db.prefix_iter(&txn, "resolve-todo|") + .map_err_with_prefix("Failed to open db prefix iterator:")?; + if let Some(Ok((todo_key, todo_value))) = cursor.next() { + (todo_key.to_string(), todo_value.to_vec()) + } else { + return Ok(false); + } + }; - if let Some(Ok((todo_key, todo_value))) = iter.next() { - let todo_key_string = String::from_utf8(todo_key.to_vec()).unwrap(); - let global_file_path = todo_key_string.strip_prefix("resolve-todo|").unwrap(); - let cpath = String::from_utf8(todo_value.to_vec()).unwrap(); - debug_print!("resolving {}", cpath); + let global_file_path = todo_key.strip_prefix("resolve-todo|").unwrap(); + let cpath = String::from_utf8(todo_value.to_vec()).unwrap(); + debug_print!("resolving {}", cpath); - // delete immediately, to make sure connect_usages() does not continue forever, even if there are errors and stuff - if let Err(e) = db.remove(&todo_key) { - tracing::error!("connect_usages() failed to remove resolve-todo key: {:?}", e); - } + ast_index.db.delete(&mut txn, &todo_key).map_err_with_prefix("Failed to delete resolve-todo| key")?; - let definitions = doc_defs(ast_index.clone(), &cpath.to_string()).await; - let batch_arc = flush_sled_batch(ast_index.clone(), 1000).await; - let mut batch = batch_arc.lock().await; + let definitions = doc_defs_internal(ast_index.clone(), &txn, &cpath); - let mut resolved_usages: Vec<(usize, String)> = vec![]; - for def in definitions { - let tmp = _connect_usages_helper(&db, ucx, &def, &mut batch).await; - resolved_usages.extend(tmp); - } - batch.insert( - format!("doc-resolved|{}", global_file_path).as_bytes(), - serde_cbor::to_vec(&resolved_usages).unwrap().as_slice(), - ); - return true; + let mut resolved_usages: Vec<(usize, String)> = vec![]; + for def in definitions { + let tmp = _connect_usages_helper(ast_index.clone(), ucx, def, &mut txn)?; + resolved_usages.extend(tmp); } - false + ast_index.db.put( + &mut txn, + &format!("doc-resolved|{}", global_file_path), + &serde_cbor::to_vec(&resolved_usages).unwrap(), + ).map_err_with_prefix("Failed to insert doc-resolved:")?; + + txn.commit().map_err_with_prefix("Failed to commit transaction:")?; + + Ok(true) } pub async fn connect_usages_look_if_full_reset_needed(ast_index: Arc) -> ConnectUsageContext { - flush_sled_batch(ast_index.clone(), 0).await; - let db = ast_index.sleddb.clone(); let class_hierarchy_key = b"class-hierarchy|"; let existing_hierarchy: IndexMap> = match db.get(class_hierarchy_key) { Ok(Some(value)) => serde_cbor::from_slice(&value).unwrap_or_default(), @@ -450,12 +453,12 @@ lazy_static! { static ref MAGNIFYING_GLASS_RE: Regex = Regex::new(r"(\w+)πŸ”Ž(\w+)").unwrap(); } -async fn _connect_usages_helper( - db: &sled::Db, +fn _connect_usages_helper<'a>( + ast_index: Arc, ucx: &mut ConnectUsageContext, - definition: &AstDefinition, - batch: &mut sled::Batch -) -> Vec<(usize, String)> { + definition: Arc, + txn: &mut RwTxn<'a>, +) -> Result, String> { // Data example: // (1) c/Animal::self_review ⚑ alt_testsuite::cpp_goat_library::Animal::self_review // (2) c/cpp_goat_library::Animal::self_review ⚑ alt_testsuite::cpp_goat_library::Animal::self_review @@ -555,10 +558,10 @@ async fn _connect_usages_helper( let c_prefix = format!("c|{}", v); debug_print!(" scanning {}", c_prefix); // println!(" c_prefix {:?} because v={:?}", c_prefix, v); - let mut c_iter = db.scan_prefix(&c_prefix); + let mut c_iter = ast_index.db.prefix_iter(txn, &c_prefix) + .map_err_with_prefix("Failed to open db range iter:")?; while let Some(Ok((c_key, _))) = c_iter.next() { - let c_key_string = String::from_utf8(c_key.to_vec()).unwrap(); - let parts: Vec<&str> = c_key_string.split(" ⚑ ").collect(); + let parts: Vec<&str> = c_key.split(" ⚑ ").collect(); if parts.len() == 2 { if parts[0] == c_prefix { let resolved_target = parts[1].trim(); @@ -583,7 +586,8 @@ async fn _connect_usages_helper( } let single_thing_found = found.into_iter().next().unwrap(); let u_key = format!("u|{} ⚑ {}", single_thing_found, official_path); - batch.insert(u_key.as_bytes(), serde_cbor::to_vec(&usage.uline).unwrap()); + ast_index.db.put(txn, &u_key, &serde_cbor::to_vec(&usage.uline).unwrap()) + .map_err_with_prefix("Failed to insert key in db:")?; debug_print!(" add {:?} <= {}", u_key, usage.uline); all_saved_ulinks.push(u_key); result.push((usage.uline, single_thing_found)); @@ -593,8 +597,9 @@ async fn _connect_usages_helper( } // for usages let cleanup_key = format!("resolve-cleanup|{}", definition.official_path.join("::")); let cleanup_value = serde_cbor::to_vec(&all_saved_ulinks).unwrap(); - batch.insert(cleanup_key.as_bytes(), cleanup_value.as_slice()); - result + ast_index.db.put(txn, &cleanup_key, cleanup_value.as_slice()) + .map_err_with_prefix("Failed to insert key in db:")?; + Ok(result) } async fn _derived_from(db: &sled::Db) -> IndexMap> @@ -987,7 +992,7 @@ mod tests { #[tokio::test] async fn test_ast_db_cpp() { init_tracing(); - let ast_index = ast_index_init("".to_string(), 10, false).await; + let ast_index = ast_index_init("".to_string(), 10).await; run_ast_db_test( ast_index, "src/ast/alt_testsuite/cpp_goat_library.h", @@ -1001,7 +1006,7 @@ mod tests { #[tokio::test] async fn test_ast_db_py() { init_tracing(); - let ast_index = ast_index_init("".to_string(), 10, false).await; + let ast_index = ast_index_init("".to_string(), 10).await; run_ast_db_test( ast_index, "src/ast/alt_testsuite/py_goat_library.py", diff --git a/refact-agent/engine/src/ast/ast_indexer_thread.rs b/refact-agent/engine/src/ast/ast_indexer_thread.rs index 260a08013..13dc00efa 100644 --- a/refact-agent/engine/src/ast/ast_indexer_thread.rs +++ b/refact-agent/engine/src/ast/ast_indexer_thread.rs @@ -304,7 +304,7 @@ pub async fn ast_indexer_block_until_finished(ast_service: Arc Arc> { - let ast_index = ast_index_init(ast_permanent, ast_max_files, false).await; + let ast_index = ast_index_init(ast_permanent, ast_max_files).await; let ast_status = Arc::new(AMutex::new(AstStatus { astate_notify: Arc::new(ANotify::new()), astate: String::from("starting"), diff --git a/refact-agent/engine/src/ast/ast_structs.rs b/refact-agent/engine/src/ast/ast_structs.rs index 82c76f1d8..dff2f0774 100644 --- a/refact-agent/engine/src/ast/ast_structs.rs +++ b/refact-agent/engine/src/ast/ast_structs.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use std::sync::Arc; use std::fmt; use serde::{Deserialize, Serialize}; +use tempfile::TempDir; use tokio::sync::{Mutex as AMutex, Notify as ANotify}; pub use crate::ast::treesitter::structs::SymbolType; @@ -58,8 +59,9 @@ impl AstDefinition { } pub struct AstDB { - pub sleddb: Arc, - pub sledbatch: Arc>, + pub db_env: Arc, + pub db: Arc>, + pub db_temp_dir: Option, pub batch_counter: AMutex, pub counters_increase: AMutex>, pub ast_max_files: usize, From 61affa3aa0637c8b6f408e4ebf58210569335ede Mon Sep 17 00:00:00 2001 From: Humberto Yusta Date: Thu, 29 May 2025 22:24:33 +0200 Subject: [PATCH 11/50] updating ast db to lmdb --- refact-agent/engine/src/ast/ast_db.rs | 321 ++++++++++++++------------ 1 file changed, 177 insertions(+), 144 deletions(-) diff --git a/refact-agent/engine/src/ast/ast_db.rs b/refact-agent/engine/src/ast/ast_db.rs index cb820c9c0..64deddeb8 100644 --- a/refact-agent/engine/src/ast/ast_db.rs +++ b/refact-agent/engine/src/ast/ast_db.rs @@ -110,26 +110,26 @@ pub async fn ast_index_init(ast_permanent: String, ast_max_files: usize) -> Arc< Arc::new(ast_index) } -pub async fn fetch_counters(ast_index: Arc) -> AstCounters +pub fn fetch_counters(ast_index: Arc) -> Result { let txn = ast_index.db_env.read_txn().unwrap(); let counter_defs = ast_index.db.get(&txn, "counters|defs") - .expect("Failed to get counters|defs") + .map_err_with_prefix("Failed to get counters|defs")? .map(|v| serde_cbor::from_slice::(&v).unwrap()) .unwrap_or(0); let counter_usages = ast_index.db.get(&txn, "counters|usages") - .expect("Failed to get counters|usages") + .map_err_with_prefix("Failed to get counters|usages")? .map(|v| serde_cbor::from_slice::(&v).unwrap()) .unwrap_or(0); let counter_docs = ast_index.db.get(&txn, "counters|docs") - .expect("Failed to get counters|docs") + .map_err_with_prefix("Failed to get counters|docs")? .map(|v| serde_cbor::from_slice::(&v).unwrap()) .unwrap_or(0); - AstCounters { + Ok(AstCounters { counter_defs, counter_usages, counter_docs, - } + }) } fn increase_counter<'a>(ast_index: Arc, txn: &mut heed::RwTxn<'a>, counter_key: &str, adjustment: i32) { @@ -215,7 +215,7 @@ pub async fn doc_add( Ok((defs.into_iter().map(Arc::new).collect(), language)) } -pub async fn doc_remove(ast_index: Arc, cpath: &String) +pub fn doc_remove(ast_index: Arc, cpath: &String) { let file_global_path = filesystem_path_to_double_colon_path(cpath); let d_prefix = format!("d|{}::", file_global_path.join("::")); @@ -398,47 +398,61 @@ pub fn connect_usages(ast_index: Arc, ucx: &mut ConnectUsageContext) -> R Ok(true) } -pub async fn connect_usages_look_if_full_reset_needed(ast_index: Arc) -> ConnectUsageContext +pub fn connect_usages_look_if_full_reset_needed(ast_index: Arc) -> Result { - let class_hierarchy_key = b"class-hierarchy|"; - let existing_hierarchy: IndexMap> = match db.get(class_hierarchy_key) { - Ok(Some(value)) => serde_cbor::from_slice(&value).unwrap_or_default(), - _ => IndexMap::new(), - }; + let class_hierarchy_key = "class-hierarchy|"; - let new_derived_from_map = _derived_from(&db).await; - let mut batch = sled::Batch::default(); + let new_derived_from_map = _derived_from(ast_index.clone()).unwrap_or_default(); + + let mut txn = ast_index.db_env.write_txn() + .map_err(|e| format!("Failed to create write transaction: {:?}", e))?; + + let existing_hierarchy: IndexMap> = match ast_index.db.get(&txn, class_hierarchy_key) { + Ok(Some(value)) => serde_cbor::from_slice(value).unwrap_or_default(), + Ok(None) => IndexMap::new(), + Err(e) => return Err(format!("Failed to get class hierarchy: {:?}", e)) + }; if existing_hierarchy.is_empty() { let serialized_hierarchy = serde_cbor::to_vec(&new_derived_from_map).unwrap(); - batch.insert(class_hierarchy_key, serialized_hierarchy.as_slice()); - // first run, do nothing because all the definitions are already in the todo list - + ast_index.db.put(&mut txn, class_hierarchy_key, &serialized_hierarchy) + .map_err_with_prefix("Failed to put class_hierarchy in db:")?; + // First run, serialize and store the new hierarchy } else if new_derived_from_map != existing_hierarchy { - // XXX first branch is not covered by tests (simple enough to work and not break?) - tracing::info!(" * * * class hierarchy changed {} classes => {} classes, all usages need to be reconnected * * *", existing_hierarchy.len(), new_derived_from_map.len()); + tracing::info!(" * * * class hierarchy changed {} classes => {} classes, all usages need to be reconnected * * *", + existing_hierarchy.len(), new_derived_from_map.len()); + let serialized_hierarchy = serde_cbor::to_vec(&new_derived_from_map).unwrap(); - batch.insert(class_hierarchy_key, serialized_hierarchy.as_slice()); - - let mut iter = db.scan_prefix("doc-cpath|"); - let mut cnt = 0; - while let Some(Ok((key, value))) = iter.next() { - let key_string = String::from_utf8(key.to_vec()).unwrap(); - if let Some(file_global_path) = key_string.strip_prefix("doc-cpath|") { - let cpath = String::from_utf8(value.to_vec()).unwrap(); - let resolve_todo_key = format!("resolve-todo|{}", file_global_path); - batch.insert(resolve_todo_key.as_bytes(), cpath.as_bytes()); - cnt += 1; + ast_index.db.put(&mut txn, class_hierarchy_key, &serialized_hierarchy) + .map_err(|e| format!("Failed to put class hierarchy: {:?}", e))?; + + let mut keys_to_update = Vec::new(); + + { + let mut cursor = ast_index.db.prefix_iter(&txn, "doc-cpath|") + .map_err(|e| format!("Failed to create prefix iterator: {:?}", e))?; + + while let Some(Ok((key, value))) = cursor.next() { + if let Some(file_global_path) = key.strip_prefix("doc-cpath|") { + let cpath = String::from_utf8(value.to_vec()) + .map_err(|e| format!("Failed to parse value as UTF-8: {:?}", e))?; + + let resolve_todo_key = format!("resolve-todo|{}", file_global_path); + keys_to_update.push((resolve_todo_key, cpath)); + } } } - tracing::info!("added {} items to resolve-todo", cnt); - } - if let Err(e) = db.apply_batch(batch) { - tracing::error!("connect_usages_look_if_full_reset_needed() failed to apply batch: {:?}", e); + tracing::info!("adding {} items to resolve-todo", keys_to_update.len()); + for (key, cpath) in keys_to_update { + ast_index.db.put(&mut txn, &key, cpath.as_bytes()) + .map_err_with_prefix("Failed to put db key to resolve-todo:")?; + } } - ConnectUsageContext { + txn.commit().map_err(|e| format!("Failed to commit transaction: {:?}", e))?; + + Ok(ConnectUsageContext { derived_from_map: new_derived_from_map, errstats: AstErrorStats::default(), usages_homeless: 0, @@ -446,7 +460,7 @@ pub async fn connect_usages_look_if_full_reset_needed(ast_index: Arc) -> usages_not_found: 0, usages_ambiguous: 0, t0: Instant::now(), - } + }) } lazy_static! { @@ -602,26 +616,32 @@ fn _connect_usages_helper<'a>( Ok(result) } -async fn _derived_from(db: &sled::Db) -> IndexMap> +fn _derived_from(ast_index: Arc) -> Result>, String> { // Data example: // classes/cppπŸ”ŽAnimal ⚑ alt_testsuite::cpp_goat_library::Goat πŸ‘‰ "cppπŸ”ŽGoat" let mut derived_map: IndexMap> = IndexMap::new(); let t_prefix = "classes|"; - let mut iter = db.scan_prefix(t_prefix); - while let Some(Ok((key, value))) = iter.next() { - let key_string = String::from_utf8(key.to_vec()).unwrap(); - let value_string = String::from_utf8(value.to_vec()).unwrap(); - let parts: Vec<&str> = key_string.split(" ⚑ ").collect(); - if parts.len() == 2 { - let parent = parts[0].trim().strip_prefix(t_prefix).unwrap_or(parts[0].trim()).to_string(); - let child = value_string.trim().to_string(); - let entry = derived_map.entry(child).or_insert_with(Vec::new); - if !entry.contains(&parent) { - entry.push(parent); + { + let txn = ast_index.db_env.read_txn() + .map_err(|e| format!("Failed to create read transaction: {:?}", e))?; + let mut cursor = ast_index.db.prefix_iter(&txn, t_prefix) + .map_err(|e| format!("Failed to create prefix iterator: {:?}", e))?; + + while let Some(Ok((key, value))) = cursor.next() { + let value_string = String::from_utf8(value.to_vec()).unwrap(); + + let parts: Vec<&str> = key.split(" ⚑ ").collect(); + if parts.len() == 2 { + let parent = parts[0].trim().strip_prefix(t_prefix).unwrap_or(parts[0].trim()).to_string(); + let child = value_string.trim().to_string(); + let entry = derived_map.entry(child).or_insert_with(Vec::new); + if !entry.contains(&parent) { + entry.push(parent); + } + } else { + tracing::warn!("bad key {key}"); } - } else { - tracing::warn!("bad key {}", key_string); } } // Have perfectly good [child, [parent1, parent2, ..]] @@ -658,63 +678,71 @@ async fn _derived_from(db: &sled::Db) -> IndexMap> build_all_derived_from(klass, &derived_map, &mut all_derived_from, &mut visited); } // now have all_derived_from {"cppπŸ”ŽCosmicGoat": ["cppπŸ”ŽCosmicJustice", "cppπŸ”ŽGoat", "cppπŸ”ŽAnimal"], "cppπŸ”ŽCosmicJustice": [], "cppπŸ”ŽGoat": ["cppπŸ”ŽAnimal"], "cppπŸ”ŽAnimal": []} - all_derived_from + Ok(all_derived_from) } -pub async fn usages(ast_index: Arc, full_official_path: String, limit_n: usize) -> Vec<(Arc, usize)> +/// The best way to get full_official_path is to call definitions() first +pub fn usages(ast_index: Arc, full_official_path: String, limit_n: usize) -> Result, usize)>, String> { - // The best way to get full_official_path is to call definitions() first - let db = ast_index.sleddb.clone(); let mut usages = Vec::new(); let u_prefix1 = format!("u|{} ", full_official_path); // this one has space let u_prefix2 = format!("u|{}", full_official_path); - let mut iter = db.scan_prefix(&u_prefix1); - while let Some(Ok((u_key, u_value))) = iter.next() { + + let txn = ast_index.db_env.read_txn() + .map_err(|e| format!("Failed to create read transaction: {:?}", e))?; + + let mut cursor = ast_index.db.prefix_iter(&txn, &u_prefix1) + .map_err(|e| format!("Failed to create prefix iterator: {:?}", e))?; + + while let Some(Ok((u_key, u_value))) = cursor.next() { if usages.len() >= limit_n { break; } - let key_string = String::from_utf8(u_key.to_vec()).unwrap(); - let uline: usize = serde_cbor::from_slice(&u_value).unwrap_or(0); // Assuming `uline` is stored in the value - let parts: Vec<&str> = key_string.split(" ⚑ ").collect(); + + let parts: Vec<&str> = u_key.split(" ⚑ ").collect(); if parts.len() == 2 && parts[0] == u_prefix2 { let full_path = parts[1].trim(); let d_key = format!("d|{}", full_path); - if let Ok(Some(d_value)) = db.get(d_key.as_bytes()) { + + if let Ok(Some(d_value)) = ast_index.db.get(&txn, &d_key) { + let uline = serde_cbor::from_slice::(&u_value).unwrap_or(0); + match serde_cbor::from_slice::(&d_value) { - Ok(definition) => { - usages.push((Arc::new(definition), uline)); - }, - Err(e) => println!("Failed to deserialize value for {}: {:?}", d_key, e), + Ok(defintion) => usages.push((Arc::new(defintion), uline)), + Err(e) => tracing::error!("Failed to deserialize value for {}: {:?}", d_key, e), } } - } else if parts.len() != 2 { - tracing::error!("usage record has more than two ⚑ key was: {}", key_string); + } else if parts.len() != 2 { + tracing::error!("usage record has more than two ⚑ key was: {}", u_key); } } - usages + + Ok(usages) } -pub async fn definitions(ast_index: Arc, double_colon_path: &str) -> Vec> +pub fn definitions(ast_index: Arc, double_colon_path: &str) -> Result>, String> { - let db = ast_index.sleddb.clone(); let c_prefix1 = format!("c|{} ", double_colon_path); // has space let c_prefix2 = format!("c|{}", double_colon_path); + + let txn = ast_index.db_env.read_txn() + .map_err_with_prefix("Failed to create read transaction:")?; + let mut path_groups: HashMap> = HashMap::new(); - // println!("definitions(c_prefix={:?})", c_prefix); - let mut iter = db.scan_prefix(&c_prefix1); - while let Some(Ok((key, _))) = iter.next() { - let key_string = String::from_utf8(key.to_vec()).unwrap(); - if key_string.contains(" ⚑ ") { - let parts: Vec<&str> = key_string.split(" ⚑ ").collect(); + let mut cursor = ast_index.db.prefix_iter(&txn, &c_prefix1) + .map_err_with_prefix("Failed to create db prefix iterator:")?; + while let Some(Ok((key, _))) = cursor.next() { + if key.contains(" ⚑ ") { + let parts: Vec<&str> = key.split(" ⚑ ").collect(); if parts.len() == 2 && parts[0] == c_prefix2 { let full_path = parts[1].trim().to_string(); let colon_count = full_path.matches("::").count(); path_groups.entry(colon_count).or_insert_with(Vec::new).push(full_path); } else if parts.len() != 2 { - tracing::error!("c-record has more than two ⚑ key was: {}", key_string); + tracing::error!("c-record has more than two ⚑ key was: {}", key); } } else { - tracing::error!("c-record doesn't have ⚑ key: {}", key_string); + tracing::error!("c-record doesn't have ⚑ key: {}", key); } } let min_colon_count = path_groups.keys().min().cloned().unwrap_or(usize::MAX); @@ -722,19 +750,19 @@ pub async fn definitions(ast_index: Arc, double_colon_path: &str) -> Vec< if let Some(paths) = path_groups.get(&min_colon_count) { for full_path in paths { let d_key = format!("d|{}", full_path); - if let Ok(Some(d_value)) = db.get(d_key.as_bytes()) { + if let Ok(Some(d_value)) = ast_index.db.get(&txn, &d_key) { match serde_cbor::from_slice::(&d_value) { Ok(definition) => defs.push(Arc::new(definition)), - Err(e) => println!("Failed to deserialize value for {}: {:?}", d_key, e), + Err(e) => return Err(format!("Failed to deserialize value for {}: {:?}", d_key, e)), } } } } - defs + Ok(defs) } #[allow(dead_code)] -pub async fn type_hierarchy(ast_index: Arc, language: String, subtree_of: String) -> String +pub fn type_hierarchy(ast_index: Arc, language: String, subtree_of: String) -> Result { // Data example: // classes/cppπŸ”ŽAnimal ⚑ alt_testsuite::cpp_goat_library::Goat πŸ‘‰ "cppπŸ”ŽGoat" @@ -754,20 +782,24 @@ pub async fn type_hierarchy(ast_index: Arc, language: String, subtree_of: // CosmicJustice // CosmicGoat // - let db = ast_index.sleddb.clone(); let t_prefix = format!("classes|{}", language); - let mut iter = db.scan_prefix(&t_prefix); let mut hierarchy_map: IndexMap> = IndexMap::new(); - while let Some(Ok((key, value))) = iter.next() { - let key_string = String::from_utf8(key.to_vec()).unwrap(); - let value_string = String::from_utf8(value.to_vec()).unwrap(); - if key_string.contains(" ⚑ ") { - let parts: Vec<&str> = key_string.split(" ⚑ ").collect(); - if parts.len() == 2 { - let parent = parts[0].trim().strip_prefix("classes|").unwrap_or(parts[0].trim()).to_string(); - let child = value_string.trim().to_string(); - hierarchy_map.entry(parent).or_insert_with(Vec::new).push(child); + { + let txn = ast_index.db_env.read_txn() + .map_err_with_prefix("Failed to create read transaction:")?; + let mut cursor = ast_index.db.prefix_iter(&txn, &t_prefix) + .map_err_with_prefix("Failed to create prefix iterator:")?; + + while let Some(Ok((key, value))) = cursor.next() { + let value_string = String::from_utf8(value.to_vec()).unwrap(); + if key.contains(" ⚑ ") { + let parts: Vec<&str> = key.split(" ⚑ ").collect(); + if parts.len() == 2 { + let parent = parts[0].trim().strip_prefix("classes|").unwrap_or(parts[0].trim()).to_string(); + let child = value_string.trim().to_string(); + hierarchy_map.entry(parent).or_insert_with(Vec::new).push(child); + } } } } @@ -795,11 +827,10 @@ pub async fn type_hierarchy(ast_index: Arc, language: String, subtree_of: result.push_str(&build_hierarchy(&hierarchy_map, &subtree_of, 0, &language)); } - result + Ok(result) } -pub async fn definition_paths_fuzzy(ast_index: Arc, pattern: &str, top_n: usize, max_candidates_to_consider: usize) -> Vec { - let db = ast_index.sleddb.clone(); +pub async fn definition_paths_fuzzy(ast_index: Arc, pattern: &str, top_n: usize, max_candidates_to_consider: usize) -> Result, String> { let mut candidates = HashSet::new(); let mut patterns_to_try = Vec::new(); @@ -816,26 +847,30 @@ pub async fn definition_paths_fuzzy(ast_index: Arc, pattern: &str, top_n: } } - for pat in patterns_to_try { - let c_prefix = format!("c|{}", pat); - let mut iter = db.scan_prefix(&c_prefix); - while let Some(Ok((key, _))) = iter.next() { - let key_string = String::from_utf8(key.to_vec()).unwrap(); - if let Some((_, dest)) = key_string.split_once(" ⚑ ") { - candidates.insert(dest.to_string()); + { + let txn = ast_index.db_env.read_txn() + .map_err_with_prefix("Failed to create read transaction:")?; + + for pat in patterns_to_try { + let mut cursor = ast_index.db.prefix_iter(&txn, &format!("c|{}", pat)) + .map_err_with_prefix("Failed to create prefix iterator:")?; + while let Some(Ok((key, _))) = cursor.next() { + if let Some((_, dest)) = key.split_once(" ⚑ ") { + candidates.insert(dest.to_string()); + } + if candidates.len() >= max_candidates_to_consider { + break; + } } if candidates.len() >= max_candidates_to_consider { break; } } - if candidates.len() >= max_candidates_to_consider { - break; - } } let results = fuzzy_search(&pattern.to_string(), candidates, top_n, &[':']); - results.into_iter() + Ok(results.into_iter() .map(|result| { if let Some(pos) = result.find("::") { result[pos + 2..].to_string() @@ -843,37 +878,39 @@ pub async fn definition_paths_fuzzy(ast_index: Arc, pattern: &str, top_n: result } }) - .collect() + .collect()) } #[allow(dead_code)] -pub async fn dump_database(ast_index: Arc) -> usize +pub fn dump_database(ast_index: Arc) -> Result { - let db = ast_index.sleddb.clone(); - println!("\nsled has {} records", db.len()); - let iter = db.iter(); + let txn = ast_index.db_env.read_txn() + .map_err_with_prefix("Failed to create read transaction:")?; + let db_len = ast_index.db.len(&txn).map_err_with_prefix("Failed to count records:")?; + println!("\ndb has {db_len} records"); + let iter = ast_index.db.iter(&txn) + .map_err_with_prefix("Failed to create iterator:")?; for item in iter { - let (key, value) = item.unwrap(); - let key_string = String::from_utf8(key.to_vec()).unwrap(); - if key_string.starts_with("d|") { + let (key, value) = item.map_err_with_prefix("Failed to get item:")?; + if key.starts_with("d|") { match serde_cbor::from_slice::(&value) { - Ok(definition) => println!("{} πŸ‘‰ {:.50}", key_string, format!("{:?}", definition)), - Err(e) => println!("Failed to deserialize value at {}: {:?}", key_string, e), + Ok(definition) => println!("{} πŸ‘‰ {:.50}", key, format!("{:?}", definition)), + Err(e) => println!("Failed to deserialize value at {}: {:?}", key, e), } - } else if key_string.starts_with("classes|") { + } else if key.starts_with("classes|") { let value_string = String::from_utf8(value.to_vec()).unwrap(); - println!("{} πŸ‘‰ {:?}", key_string, value_string); - } else if key_string.starts_with("counters|") { + println!("{} πŸ‘‰ {:?}", key, value_string); + } else if key.starts_with("counters|") { let counter_value: i32 = serde_cbor::from_slice(&value).unwrap(); - println!("{}: {}", key_string, counter_value); + println!("{}: {}", key, counter_value); } else if value.len() > 0 { - println!("{} ({} bytes)", key_string, value.len()); + println!("{} ({} bytes)", key, value.len()); } else { - println!("{}", key_string); + println!("{}", key); } } println!("dump_database over"); - db.len() + Ok(db_len) } @@ -916,18 +953,17 @@ mod tests { println!("(E) {}:{} {}", error.err_cpath, error.err_line, error.err_message); } - let mut ucx: ConnectUsageContext = connect_usages_look_if_full_reset_needed(ast_index.clone()).await; + let mut ucx: ConnectUsageContext = connect_usages_look_if_full_reset_needed(ast_index.clone()).unwrap(); loop { - let did_anything = connect_usages(ast_index.clone(), &mut ucx).await; + let did_anything = connect_usages(ast_index.clone(), &mut ucx).unwrap(); if !did_anything { break; } } - flush_sled_batch(ast_index.clone(), 0).await; - dump_database(ast_index.clone()).await; + let _ = dump_database(ast_index.clone()).unwrap(); - let hierarchy = type_hierarchy(ast_index.clone(), language.to_string(), "".to_string()).await; + let hierarchy = type_hierarchy(ast_index.clone(), language.to_string(), "".to_string()).unwrap(); println!("Type hierarchy:\n{}", hierarchy); let expected_hierarchy = "Animal\n Goat\n CosmicGoat\nCosmicJustice\n CosmicGoat\n"; assert_eq!( @@ -936,11 +972,11 @@ mod tests { ); println!( "Type hierachy subtree_of=Animal:\n{}", - type_hierarchy(ast_index.clone(), language.to_string(), format!("{}πŸ”ŽAnimal", language)).await + type_hierarchy(ast_index.clone(), language.to_string(), format!("{}πŸ”ŽAnimal", language)).unwrap() ); // Goat::Goat() is a C++ constructor - let goat_def = definitions(ast_index.clone(), goat_location).await; + let goat_def = definitions(ast_index.clone(), goat_location).unwrap(); let mut goat_def_str = String::new(); for def in goat_def.iter() { goat_def_str.push_str(&format!("{:?}\n", def)); @@ -948,9 +984,9 @@ mod tests { println!("goat_def_str:\n{}", goat_def_str); assert!(goat_def.len() == 1); - let animalage_defs = definitions(ast_index.clone(), animal_age_location).await; + let animalage_defs = definitions(ast_index.clone(), animal_age_location).unwrap(); let animalage_def0 = animalage_defs.first().unwrap(); - let animalage_usage = usages(ast_index.clone(), animalage_def0.path(), 100).await; + let animalage_usage = usages(ast_index.clone(), animalage_def0.path(), 100).unwrap(); let mut animalage_usage_str = String::new(); for (used_at_def, used_at_uline) in animalage_usage.iter() { animalage_usage_str.push_str(&format!("{:}:{}\n", used_at_def.cpath, used_at_uline)); @@ -968,24 +1004,21 @@ mod tests { println!("goat_usage:\n{}", goat_usage_str); assert!(goat_usage.len() == 1 || goat_usage.len() == 2); // derived from generates usages (new style: py) or not (old style) - doc_remove(ast_index.clone(), &library_file_path.to_string()).await; - doc_remove(ast_index.clone(), &main_file_path.to_string()).await; - flush_sled_batch(ast_index.clone(), 0).await; + doc_remove(ast_index.clone(), &library_file_path.to_string()); + doc_remove(ast_index.clone(), &main_file_path.to_string()); - let dblen = dump_database(ast_index.clone()).await; - let counters = fetch_counters(ast_index.clone()).await; + let dblen = dump_database(ast_index.clone()).unwrap(); + let counters = fetch_counters(ast_index.clone()).unwrap(); assert_eq!(counters.counter_defs, 0); assert_eq!(counters.counter_usages, 0); assert_eq!(counters.counter_docs, 0); assert_eq!(dblen, 3 + 1); // 3 counters and 1 class hierarchy - let db = ast_index.sleddb.clone(); - drop(ast_index); // assert!(Arc::strong_count(&db) == 1); - println!("db.flush"); - let x = db.flush().unwrap(); - println!("db.flush returned {}, drop", x); - drop(db); + println!("db.clear"); + let mut txn = ast_index.db_env.write_txn().unwrap(); + ast_index.db.clear(&mut txn).unwrap(); + drop(ast_index); tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; } From 6b3ae6312bc0602a976409d82eaf82fe4a45a370 Mon Sep 17 00:00:00 2001 From: Humberto Yusta Date: Fri, 30 May 2025 17:55:14 +0200 Subject: [PATCH 12/50] fix: windows warning import --- refact-agent/engine/src/integrations/integr_cmdline.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/refact-agent/engine/src/integrations/integr_cmdline.rs b/refact-agent/engine/src/integrations/integr_cmdline.rs index 7d9680073..532d823b5 100644 --- a/refact-agent/engine/src/integrations/integr_cmdline.rs +++ b/refact-agent/engine/src/integrations/integr_cmdline.rs @@ -8,7 +8,6 @@ use serde::Serialize; use async_trait::async_trait; use tokio::process::Command; use tracing::info; -use std::borrow::Cow; #[cfg(not(target_os = "windows"))] use shell_escape::escape; @@ -144,7 +143,7 @@ pub fn replace_args(x: &str, args_str: &HashMap) -> String { #[cfg(target_os = "windows")] let x = powershell_escape(value); #[cfg(not(target_os = "windows"))] - let x = escape(Cow::from(value.as_str())).to_string(); + let x = escape(std::borrow::Cow::from(value.as_str())).to_string(); x }; result = result.replace(&format!("%{}%", key), &escaped_value); From deb55b7e32a71733cf1273a67364fd9f5572dd9e Mon Sep 17 00:00:00 2001 From: Humberto Yusta Date: Fri, 30 May 2025 17:58:12 +0200 Subject: [PATCH 13/50] fix: keep temp dir in ast struct, so it can be cleaned up at drop --- refact-agent/engine/src/ast/ast_db.rs | 4 +--- refact-agent/engine/src/ast/ast_structs.rs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/refact-agent/engine/src/ast/ast_db.rs b/refact-agent/engine/src/ast/ast_db.rs index 64deddeb8..c1c823e3e 100644 --- a/refact-agent/engine/src/ast/ast_db.rs +++ b/refact-agent/engine/src/ast/ast_db.rs @@ -102,9 +102,7 @@ pub async fn ast_index_init(ast_permanent: String, ast_max_files: usize) -> Arc< let ast_index = AstDB { db_env, db, - db_temp_dir, - batch_counter: AMutex::new(0), - counters_increase: AMutex::new(HashMap::new()), + _db_temp_dir: db_temp_dir, ast_max_files, }; Arc::new(ast_index) diff --git a/refact-agent/engine/src/ast/ast_structs.rs b/refact-agent/engine/src/ast/ast_structs.rs index dff2f0774..d36dc7ff0 100644 --- a/refact-agent/engine/src/ast/ast_structs.rs +++ b/refact-agent/engine/src/ast/ast_structs.rs @@ -61,9 +61,7 @@ impl AstDefinition { pub struct AstDB { pub db_env: Arc, pub db: Arc>, - pub db_temp_dir: Option, - pub batch_counter: AMutex, - pub counters_increase: AMutex>, + pub _db_temp_dir: Option, // Kept for cleanup pub ast_max_files: usize, } From b4f39ee10e9154fae16b4aeb3c70e327944d8b3e Mon Sep 17 00:00:00 2001 From: Humberto Yusta Date: Fri, 30 May 2025 18:01:29 +0200 Subject: [PATCH 14/50] new ast db usage fixes, tracing improvements and refactor usages --- refact-agent/engine/src/ast/ast_db.rs | 32 +++++++++++++------ .../engine/src/ast/ast_indexer_thread.rs | 19 +++++------ refact-agent/engine/src/ast/ast_structs.rs | 4 +-- .../src/at_commands/at_ast_definition.rs | 6 ++-- .../src/at_commands/at_ast_reference.rs | 10 ++++-- .../engine/src/at_commands/at_tree.rs | 15 ++++----- refact-agent/engine/src/custom_error.rs | 6 ++++ .../engine/src/http/routers/v1/ast.rs | 2 +- .../engine/src/http/routers/v1/at_tools.rs | 6 ++-- .../engine/src/http/routers/v1/code_lens.rs | 2 +- .../engine/src/postprocessing/pp_utils.rs | 4 +-- .../scratchpads/code_completion_replace.rs | 2 +- .../engine/src/scratchpads/completon_rag.rs | 3 +- .../engine/src/tools/tool_ast_definition.rs | 20 ++++++------ .../engine/src/tools/tool_ast_reference.rs | 11 ++++--- refact-agent/engine/src/tools/tool_cat.rs | 2 +- 16 files changed, 85 insertions(+), 59 deletions(-) diff --git a/refact-agent/engine/src/ast/ast_db.rs b/refact-agent/engine/src/ast/ast_db.rs index c1c823e3e..8dda5c511 100644 --- a/refact-agent/engine/src/ast/ast_db.rs +++ b/refact-agent/engine/src/ast/ast_db.rs @@ -2,10 +2,8 @@ use std::path::PathBuf; use std::time::Instant; use std::collections::{HashMap, HashSet}; use std::sync::Arc; -use futures::io::Cursor; use heed::{RoTxn, RwTxn}; use indexmap::IndexMap; -use tokio::sync::Mutex as AMutex; use tokio::task; use serde_cbor; use lazy_static::lazy_static; @@ -225,8 +223,7 @@ pub fn doc_remove(ast_index: Arc, cpath: &String) { let mut cursor = ast_index.db.prefix_iter(&txn, &d_prefix)?; - while let Some(Ok((key, value))) = cursor.next() { - let d_key = key.clone(); + while let Some(Ok((d_key, value))) = cursor.next() { if let Ok(definition) = serde_cbor::from_slice::(&value) { let mut path_parts: Vec<&str> = definition.official_path.iter().map(|s| s.as_str()).collect(); let official_path = definition.official_path.join("::"); @@ -345,7 +342,6 @@ pub async fn doc_usages(ast_index: Arc, cpath: &String) -> Vec<(usize, St usages } - pub struct ConnectUsageContext { pub derived_from_map: IndexMap>, pub errstats: AstErrorStats, @@ -356,6 +352,20 @@ pub struct ConnectUsageContext { pub t0: Instant, } +impl Default for ConnectUsageContext { + fn default() -> Self { + ConnectUsageContext { + derived_from_map: IndexMap::default(), + errstats: AstErrorStats::default(), + usages_homeless: 0, + usages_connected: 0, + usages_not_found: 0, + usages_ambiguous: 0, + t0: Instant::now(), + } + } +} + pub fn connect_usages(ast_index: Arc, ucx: &mut ConnectUsageContext) -> Result { let mut txn = ast_index.db_env.write_txn() @@ -992,9 +1002,9 @@ mod tests { println!("animalage_usage_str:\n{}", animalage_usage_str); assert!(animalage_usage.len() == 5); - let goat_defs = definitions(ast_index.clone(), format!("{}_goat_library::Goat", language).as_str()).await; + let goat_defs = definitions(ast_index.clone(), format!("{}_goat_library::Goat", language).as_str()).unwrap(); let goat_def0 = goat_defs.first().unwrap(); - let goat_usage = usages(ast_index.clone(), goat_def0.path(), 100).await; + let goat_usage = usages(ast_index.clone(), goat_def0.path(), 100).unwrap(); let mut goat_usage_str = String::new(); for (used_at_def, used_at_uline) in goat_usage.iter() { goat_usage_str.push_str(&format!("{:}:{}\n", used_at_def.cpath, used_at_uline)); @@ -1014,9 +1024,11 @@ mod tests { // assert!(Arc::strong_count(&db) == 1); println!("db.clear"); - let mut txn = ast_index.db_env.write_txn().unwrap(); - ast_index.db.clear(&mut txn).unwrap(); - drop(ast_index); + { + let mut txn = ast_index.db_env.write_txn().unwrap(); + ast_index.db.clear(&mut txn).unwrap(); + } + assert!(Arc::try_unwrap(ast_index).is_ok()); tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; } diff --git a/refact-agent/engine/src/ast/ast_indexer_thread.rs b/refact-agent/engine/src/ast/ast_indexer_thread.rs index 13dc00efa..29723c5b3 100644 --- a/refact-agent/engine/src/ast/ast_indexer_thread.rs +++ b/refact-agent/engine/src/ast/ast_indexer_thread.rs @@ -5,11 +5,12 @@ use tokio::sync::{Mutex as AMutex, Notify as ANotify}; use tokio::sync::RwLock as ARwLock; use tokio::task::JoinHandle; use tracing::info; +use crate::custom_error::trace_and_default; use crate::files_in_workspace::Document; use crate::global_context::GlobalContext; use crate::ast::ast_structs::{AstDB, AstStatus, AstCounters, AstErrorStats}; -use crate::ast::ast_db::{ast_index_init, fetch_counters, doc_add, doc_remove, flush_sled_batch, ConnectUsageContext, connect_usages, connect_usages_look_if_full_reset_needed}; +use crate::ast::ast_db::{ast_index_init, fetch_counters, doc_add, doc_remove, connect_usages, connect_usages_look_if_full_reset_needed}; pub struct AstIndexService { @@ -75,7 +76,7 @@ async fn ast_indexer_thread( }; let mut doc = Document { doc_path: cpath.clone().into(), doc_text: None }; - doc_remove(ast_index.clone(), &cpath).await; + doc_remove(ast_index.clone(), &cpath); match crate::files_in_workspace::get_file_text_from_memory_or_disk(gcx.clone(), &doc.doc_path).await { Ok(file_text) => { @@ -114,7 +115,7 @@ async fn ast_indexer_thread( } if stats_update_ts.elapsed() >= std::time::Duration::from_millis(1000) { // can't be lower, because flush_sled_batch() happens not very often at all - let counters: AstCounters = fetch_counters(ast_index.clone()).await; + let counters = fetch_counters(ast_index.clone()).unwrap_or_else(trace_and_default); { let mut status_locked = ast_status.lock().await; status_locked.files_unparsed = left_todo_count; @@ -136,8 +137,6 @@ async fn ast_indexer_thread( continue; } - flush_sled_batch(ast_index.clone(), 0).await; // otherwise bad stats - if !reported_parse_stats { if !stats_parsing_errors.errors.is_empty() { let error_count = stats_parsing_errors.errors_counter; @@ -188,7 +187,7 @@ async fn ast_indexer_thread( stats_parsed_cnt = 0; stats_symbols_cnt = 0; reported_parse_stats = true; - let counters: AstCounters = fetch_counters(ast_index.clone()).await; + let counters: AstCounters = fetch_counters(ast_index.clone()).unwrap_or_else(trace_and_default); { let mut status_locked = ast_status.lock().await; status_locked.files_unparsed = 0; @@ -201,20 +200,18 @@ async fn ast_indexer_thread( } // Connect usages, unless we have files in the todo - let mut usagecx: ConnectUsageContext = connect_usages_look_if_full_reset_needed(ast_index.clone()).await; + let mut usagecx = connect_usages_look_if_full_reset_needed(ast_index.clone()).unwrap_or_else(trace_and_default); loop { todo_count = ast_service.lock().await.ast_todo.len(); if todo_count > 0 { break; } - let did_anything = connect_usages(ast_index.clone(), &mut usagecx).await; + let did_anything = connect_usages(ast_index.clone(), &mut usagecx).unwrap_or_else(trace_and_default); if !did_anything { break; } } - flush_sled_batch(ast_index.clone(), 0).await; - if !usagecx.errstats.errors.is_empty() { let error_count = usagecx.errstats.errors_counter; let display_count = std::cmp::min(5, error_count); @@ -243,7 +240,7 @@ async fn ast_indexer_thread( } if !reported_connect_stats { - let counters: AstCounters = fetch_counters(ast_index.clone()).await; + let counters: AstCounters = fetch_counters(ast_index.clone()).unwrap_or_else(trace_and_default); { let mut status_locked = ast_status.lock().await; status_locked.files_unparsed = 0; diff --git a/refact-agent/engine/src/ast/ast_structs.rs b/refact-agent/engine/src/ast/ast_structs.rs index d36dc7ff0..c68a218ca 100644 --- a/refact-agent/engine/src/ast/ast_structs.rs +++ b/refact-agent/engine/src/ast/ast_structs.rs @@ -1,9 +1,8 @@ -use std::collections::HashMap; use std::sync::Arc; use std::fmt; use serde::{Deserialize, Serialize}; use tempfile::TempDir; -use tokio::sync::{Mutex as AMutex, Notify as ANotify}; +use tokio::sync::{Notify as ANotify}; pub use crate::ast::treesitter::structs::SymbolType; @@ -79,6 +78,7 @@ pub struct AstStatus { pub ast_max_files_hit: bool, } +#[derive(Default, Debug)] pub struct AstCounters { pub counter_defs: i32, pub counter_usages: i32, diff --git a/refact-agent/engine/src/at_commands/at_ast_definition.rs b/refact-agent/engine/src/at_commands/at_ast_definition.rs index 9a7caab07..7a7413dd7 100644 --- a/refact-agent/engine/src/at_commands/at_ast_definition.rs +++ b/refact-agent/engine/src/at_commands/at_ast_definition.rs @@ -2,9 +2,11 @@ use std::sync::Arc; use async_trait::async_trait; use tokio::sync::Mutex as AMutex; +use crate::ast::ast_db::definition_paths_fuzzy; use crate::at_commands::at_commands::{AtCommand, AtCommandsContext, AtParam}; use crate::call_validation::{ContextFile, ContextEnum}; use crate::at_commands::execute_at::{AtCommandMember, correct_at_arg}; +use crate::custom_error::trace_and_default; // use strsim::jaro_winkler; @@ -78,7 +80,7 @@ impl AtParam for AtParamSymbolPathQuery { } let ast_index = ast_service_opt.unwrap().lock().await.ast_index.clone(); - crate::ast::ast_db::definition_paths_fuzzy(ast_index, value, top_n, 1000).await + definition_paths_fuzzy(ast_index, value, top_n, 1000).await.unwrap_or_else(trace_and_default) } fn param_completion_valid(&self) -> bool { @@ -116,7 +118,7 @@ impl AtCommand for AtAstDefinition { let ast_service_opt = gcx.read().await.ast_service.clone(); if let Some(ast_service) = ast_service_opt { let ast_index = ast_service.lock().await.ast_index.clone(); - let defs: Vec> = crate::ast::ast_db::definitions(ast_index, arg_symbol.text.as_str()).await; + let defs: Vec> = crate::ast::ast_db::definitions(ast_index, arg_symbol.text.as_str())?; let file_paths = defs.iter().map(|x| x.cpath.clone()).collect::>(); let short_file_paths = crate::files_correction::shortify_paths(gcx.clone(), &file_paths).await; diff --git a/refact-agent/engine/src/at_commands/at_ast_reference.rs b/refact-agent/engine/src/at_commands/at_ast_reference.rs index 44f0d7c49..87e4af2fb 100644 --- a/refact-agent/engine/src/at_commands/at_ast_reference.rs +++ b/refact-agent/engine/src/at_commands/at_ast_reference.rs @@ -7,6 +7,7 @@ use crate::at_commands::at_commands::{AtCommand, AtCommandsContext, AtParam}; use crate::call_validation::{ContextFile, ContextEnum}; use crate::at_commands::execute_at::{AtCommandMember, correct_at_arg}; use crate::at_commands::at_ast_definition::AtParamSymbolPathQuery; +use crate::custom_error::trace_and_default; pub struct AtAstReference { @@ -55,14 +56,19 @@ impl AtCommand for AtAstReference { if let Some(ast_service) = ast_service_opt { let ast_index = ast_service.lock().await.ast_index.clone(); - let defs = crate::ast::ast_db::definitions(ast_index.clone(), arg_symbol.text.as_str()).await; + let defs = crate::ast::ast_db::definitions(ast_index.clone(), arg_symbol.text.as_str()) + .unwrap_or_else(trace_and_default); let mut all_results = vec![]; let mut messages = vec![]; const USAGES_LIMIT: usize = 20; if let Some(def) = defs.get(0) { - let usages: Vec<(Arc, usize)> = crate::ast::ast_db::usages(ast_index.clone(), def.path(), 100).await; + let usages: Vec<(Arc, usize)> = crate::ast::ast_db::usages( + ast_index.clone(), + def.path(), + 100, + ).unwrap_or_else(trace_and_default); let usage_count = usages.len(); let text = format!( diff --git a/refact-agent/engine/src/at_commands/at_tree.rs b/refact-agent/engine/src/at_commands/at_tree.rs index 3fa05f881..94deb8a73 100644 --- a/refact-agent/engine/src/at_commands/at_tree.rs +++ b/refact-agent/engine/src/at_commands/at_tree.rs @@ -3,7 +3,6 @@ use std::path::PathBuf; use std::sync::{Arc, RwLock}; use async_trait::async_trait; -use sled::Db; use tokio::sync::Mutex as AMutex; use tracing::warn; @@ -140,9 +139,9 @@ impl TreeNode { } } -fn _print_symbols(db: Arc, path: &PathBuf) -> String { +fn _print_symbols(db: Arc, path: &PathBuf) -> String { let cpath = path.to_string_lossy().to_string(); - let defs = crate::ast::ast_db::doc_def_internal(db.clone(), &cpath); + let defs = crate::ast::ast_db::doc_defs(db.clone(), &cpath); let symbols_list = defs .iter() .filter(|x| match x.symbol_type { @@ -165,7 +164,7 @@ async fn _print_files_tree( path: PathBuf, depth: usize, maxdepth: usize, - db_mb: Option>, + ast_db: Option>, ) -> Option { if depth > maxdepth { return None; @@ -174,8 +173,8 @@ async fn _print_files_tree( let indent = " ".repeat(depth); let name = path.file_name().unwrap_or_default().to_string_lossy().to_string(); if !node.is_dir() { - if let Some(db) = &db_mb { - output.push_str(&format!("{}{}{}\n", indent, name, _print_symbols(db.clone(), &path))); + if let Some(db) = ast_db.clone() { + output.push_str(&format!("{}{}{}\n", indent, name, _print_symbols(db, &path))); } else { output.push_str(&format!("{}{}\n", indent, name)); } @@ -189,7 +188,7 @@ async fn _print_files_tree( for (name, child) in &node.children { let mut child_path = path.clone(); child_path.push(name); - if let Some(child_str) = traverse(child, child_path, depth + 1, maxdepth, db_mb.clone()) { + if let Some(child_str) = traverse(child, child_path, depth + 1, maxdepth, ast_db.clone()) { child_output.push_str(&child_str); } else { dirs += child.is_dir() as usize; @@ -208,7 +207,7 @@ async fn _print_files_tree( let mut result = String::new(); for (name, node) in &tree.children { let db_mb = if let Some(ast) = ast_db.clone() { - Some(ast.sleddb.clone()) + Some(ast) } else { None }; diff --git a/refact-agent/engine/src/custom_error.rs b/refact-agent/engine/src/custom_error.rs index 191c5b763..6733329c1 100644 --- a/refact-agent/engine/src/custom_error.rs +++ b/refact-agent/engine/src/custom_error.rs @@ -98,3 +98,9 @@ impl MapErrToString for Result { self.map_err(|e| format!("{pref} {e}")) } } + +/// Does tracing::error!(), and returns the default value +pub fn trace_and_default(e: E) -> T { + tracing::error!("{e}"); + Default::default() +} diff --git a/refact-agent/engine/src/http/routers/v1/ast.rs b/refact-agent/engine/src/http/routers/v1/ast.rs index 30f0a3c30..3c16f254a 100644 --- a/refact-agent/engine/src/http/routers/v1/ast.rs +++ b/refact-agent/engine/src/http/routers/v1/ast.rs @@ -123,7 +123,7 @@ pub async fn handle_v1_ast_file_symbols( let search_res = match &ast_service_opt { Some(ast_service) => { let ast_index = ast_service.lock().await.ast_index.clone(); - crate::ast::ast_db::doc_defs(ast_index.clone(), &doc.doc_path.to_string_lossy().to_string()).await + crate::ast::ast_db::doc_defs(ast_index.clone(), &doc.doc_path.to_string_lossy().to_string()) } None => { return Err(ScratchError::new( diff --git a/refact-agent/engine/src/http/routers/v1/at_tools.rs b/refact-agent/engine/src/http/routers/v1/at_tools.rs index 7f9f0ccf3..ac667b5ac 100644 --- a/refact-agent/engine/src/http/routers/v1/at_tools.rs +++ b/refact-agent/engine/src/http/routers/v1/at_tools.rs @@ -163,8 +163,8 @@ pub async fn handle_v1_tools_check_if_confirmation_needed( .body(Body::from(body)) .unwrap() } - - + + let post = serde_json::from_slice::(&body_bytes) .map_err(|e| ScratchError::new(StatusCode::UNPROCESSABLE_ENTITY, format!("JSON problem: {}", e)))?; @@ -258,7 +258,7 @@ pub async fn handle_v1_tools_check_if_confirmation_needed( _ => {}, } } - + Ok(reply(!result_messages.is_empty(), &result_messages)) } diff --git a/refact-agent/engine/src/http/routers/v1/code_lens.rs b/refact-agent/engine/src/http/routers/v1/code_lens.rs index 07e6dfd19..2f212172f 100644 --- a/refact-agent/engine/src/http/routers/v1/code_lens.rs +++ b/refact-agent/engine/src/http/routers/v1/code_lens.rs @@ -66,7 +66,7 @@ pub async fn handle_v1_code_lens( let defs: Vec> = if let Some(ast_service) = ast_service_opt { let indexing_finished = crate::ast::ast_indexer_thread::ast_indexer_block_until_finished(ast_service.clone(), 300, true).await; let ast_index = ast_service.lock().await.ast_index.clone(); - let defs = crate::ast::ast_db::doc_defs(ast_index, &cpath_str).await; + let defs = crate::ast::ast_db::doc_defs(ast_index, &cpath_str); if !indexing_finished || defs.len() <= 1 { tracing::info!("indexing_finished={} defs.len()=={}", indexing_finished, defs.len()); if let Some(cache_entry) = codelens_cache.lock().await.store.get(&cpath_str) { diff --git a/refact-agent/engine/src/postprocessing/pp_utils.rs b/refact-agent/engine/src/postprocessing/pp_utils.rs index 827d50422..277bcb995 100644 --- a/refact-agent/engine/src/postprocessing/pp_utils.rs +++ b/refact-agent/engine/src/postprocessing/pp_utils.rs @@ -121,12 +121,12 @@ pub async fn pp_ast_markup_files( let defs = match &ast_service { Some(ast) if get_ast_parser_by_filename(&doc.doc_path).is_ok() => { let ast_index = ast.lock().await.ast_index.clone(); - crate::ast::ast_db::doc_defs(ast_index, &doc.doc_path.to_string_lossy().to_string()).await + crate::ast::ast_db::doc_defs(ast_index, &doc.doc_path.to_string_lossy().to_string()) } _ => vec![], }; let mut symbols_sorted_by_path_len = defs.clone(); - symbols_sorted_by_path_len.sort_by_key(|s| s.official_path.len()); + symbols_sorted_by_path_len.sort_by_key(|s| s.path().len()); result.push(Arc::new(PPFile { // doesn't matter what size the output vector is symbols_sorted_by_path_len, file_content: text, diff --git a/refact-agent/engine/src/scratchpads/code_completion_replace.rs b/refact-agent/engine/src/scratchpads/code_completion_replace.rs index 4e7f22790..cac36770d 100644 --- a/refact-agent/engine/src/scratchpads/code_completion_replace.rs +++ b/refact-agent/engine/src/scratchpads/code_completion_replace.rs @@ -164,7 +164,7 @@ pub async fn get_cursor_symbol_from_doc( let cpath_str = cpath.to_string_lossy().to_string(); ast_indexer_enqueue_files(ast_service.clone(), &vec![cpath_str.clone()], true).await; ast_indexer_block_until_finished(ast_service.clone(), 20, true).await; - let doc_syms = doc_defs(ast_index, &cpath_str).await; + let doc_syms = doc_defs(ast_index, &cpath_str); doc_syms .iter() .filter( diff --git a/refact-agent/engine/src/scratchpads/completon_rag.rs b/refact-agent/engine/src/scratchpads/completon_rag.rs index 25e012514..6b1442dea 100644 --- a/refact-agent/engine/src/scratchpads/completon_rag.rs +++ b/refact-agent/engine/src/scratchpads/completon_rag.rs @@ -1,6 +1,7 @@ use crate::ast::ast_indexer_thread::AstIndexService; use crate::ast::ast_structs::{AstDB, AstDefinition}; use crate::call_validation::{ContextFile, CursorPosition, PostprocessSettings}; +use crate::custom_error::trace_and_default; use crate::global_context::GlobalContext; use crate::postprocessing::pp_context_files::postprocess_context_files; use crate::scratchpad_abstract::HasTokenizerAndEot; @@ -120,7 +121,7 @@ async fn _cursor_position_to_context_file( info!("adding {} to context", double_colon_path); } let defs: Vec> = - crate::ast::ast_db::definitions(ast_index.clone(), double_colon_path.as_str()).await; + crate::ast::ast_db::definitions(ast_index.clone(), double_colon_path.as_str()).unwrap_or_else(trace_and_default); if defs.len() != 1 { tracing::warn!( "hmm, number of definitions for {} is {} which is not one", diff --git a/refact-agent/engine/src/tools/tool_ast_definition.rs b/refact-agent/engine/src/tools/tool_ast_definition.rs index b9eb74730..2668f0c69 100644 --- a/refact-agent/engine/src/tools/tool_ast_definition.rs +++ b/refact-agent/engine/src/tools/tool_ast_definition.rs @@ -7,6 +7,7 @@ use tokio::sync::Mutex as AMutex; use crate::at_commands::at_commands::AtCommandsContext; use crate::ast::ast_structs::AstDB; use crate::ast::ast_db::fetch_counters; +use crate::custom_error::trace_and_default; use crate::tools::tools_description::{Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum, ContextFile}; @@ -17,7 +18,7 @@ pub struct ToolAstDefinition { #[async_trait] impl Tool for ToolAstDefinition { fn as_any(&self) -> &dyn std::any::Any { self } - + async fn tool_execute( &mut self, ccx: Arc>, @@ -47,12 +48,12 @@ impl Tool for ToolAstDefinition { let ast_index = ast_service.lock().await.ast_index.clone(); crate::ast::ast_indexer_thread::ast_indexer_block_until_finished(ast_service.clone(), 20_000, true).await; - + let mut all_messages = Vec::new(); let mut all_context_files = Vec::new(); - + for symbol in symbols { - let defs = crate::ast::ast_db::definitions(ast_index.clone(), &symbol).await; + let defs = crate::ast::ast_db::definitions(ast_index.clone(), &symbol).unwrap_or_default(); let file_paths = defs.iter().map(|x| x.cpath.clone()).collect::>(); let short_file_paths = crate::files_correction::shortify_paths(gcx.clone(), &file_paths).await; @@ -78,11 +79,11 @@ impl Tool for ToolAstDefinition { usefulness: 100.0, }) }).collect(); - + if defs.len() > DEFS_LIMIT { tool_message.push_str(&format!("...and {} more\n", defs.len() - DEFS_LIMIT)); } - + all_messages.push(tool_message); all_context_files.extend(context_files); } else { @@ -100,7 +101,7 @@ impl Tool for ToolAstDefinition { tool_call_id: tool_call_id.clone(), ..Default::default() })); - + Ok((corrections, all_context_files)) } else { Err("attempt to use search_symbol_definition with no ast turned on".to_string()) @@ -139,10 +140,11 @@ pub async fn there_are_definitions_with_similar_names_though( symbol: &str, ) -> String { let fuzzy_matches: Vec = crate::ast::ast_db::definition_paths_fuzzy(ast_index.clone(), symbol, 20, 5000) - .await; + .await + .unwrap_or_else(trace_and_default); let tool_message = if fuzzy_matches.is_empty() { - let counters = fetch_counters(ast_index).await; + let counters = fetch_counters(ast_index).unwrap_or_else(trace_and_default); format!("No definitions with name `{}` found in the workspace, and no similar names were found among {} definitions in the AST tree.\n", symbol, counters.counter_defs) } else { let mut msg = format!( diff --git a/refact-agent/engine/src/tools/tool_ast_reference.rs b/refact-agent/engine/src/tools/tool_ast_reference.rs index 859e4aaa0..cb848d3e6 100644 --- a/refact-agent/engine/src/tools/tool_ast_reference.rs +++ b/refact-agent/engine/src/tools/tool_ast_reference.rs @@ -6,6 +6,7 @@ use serde_json::Value; use tokio::sync::Mutex as AMutex; use crate::at_commands::at_commands::AtCommandsContext; +use crate::custom_error::trace_and_default; use crate::tools::tools_description::{Tool, ToolDesc, ToolParam, ToolSource, ToolSourceType}; use crate::call_validation::{ChatMessage, ChatContent, ContextEnum, ContextFile}; use crate::tools::tool_ast_definition::there_are_definitions_with_similar_names_though; @@ -17,7 +18,7 @@ pub struct ToolAstReference { #[async_trait] impl Tool for ToolAstReference { fn as_any(&self) -> &dyn std::any::Any { self } - + async fn tool_execute( &mut self, ccx: Arc>, @@ -47,7 +48,7 @@ impl Tool for ToolAstReference { let ast_index = ast_service.lock().await.ast_index.clone(); crate::ast::ast_indexer_thread::ast_indexer_block_until_finished(ast_service.clone(), 20_000, true).await; - + let mut all_results = vec![]; let mut all_messages = vec![]; @@ -55,12 +56,12 @@ impl Tool for ToolAstReference { const DEFS_LIMIT: usize = 5; for symbol in symbols { - let defs = crate::ast::ast_db::definitions(ast_index.clone(), &symbol).await; + let defs = crate::ast::ast_db::definitions(ast_index.clone(), &symbol).unwrap_or_else(trace_and_default); let mut symbol_messages = vec![]; if !defs.is_empty() { for (_i, def) in defs.iter().take(DEFS_LIMIT).enumerate() { - let usedin_and_uline = crate::ast::ast_db::usages(ast_index.clone(), def.path(), 100).await; + let usedin_and_uline = crate::ast::ast_db::usages(ast_index.clone(), def.path(), 100).unwrap_or_else(trace_and_default); let file_paths = usedin_and_uline.iter().map(|(usedin, _)| usedin.cpath.clone()).collect::>(); let short_file_paths = crate::files_correction::shortify_paths(gcx.clone(), &file_paths).await; @@ -113,7 +114,7 @@ impl Tool for ToolAstReference { let fuzzy_message = there_are_definitions_with_similar_names_though(ast_index.clone(), &symbol).await; symbol_messages.push(format!("For symbol `{}`:\n{}", symbol, fuzzy_message)); } - + all_messages.push(format!("Results for symbol `{}`:\n{}", symbol, symbol_messages.join("\n"))); } diff --git a/refact-agent/engine/src/tools/tool_cat.rs b/refact-agent/engine/src/tools/tool_cat.rs index 36c2d32e6..38915fcd2 100644 --- a/refact-agent/engine/src/tools/tool_cat.rs +++ b/refact-agent/engine/src/tools/tool_cat.rs @@ -301,7 +301,7 @@ pub async fn paths_and_symbols_to_cat_with_path_ranges( let original_path = corrected_path_to_original.get(p).unwrap_or(p); let line_range = path_line_ranges.get(original_path).cloned().flatten(); - let doc_syms = crate::ast::ast_db::doc_defs(ast_index.clone(), &p).await; + let doc_syms = crate::ast::ast_db::doc_defs(ast_index.clone(), &p); // s.name() means the last part of the path // symbols.contains means exact match in comma-separated list let mut syms_def_in_this_file = vec![]; From 42feca501abd8c208825a2b301c2e6367ba397c8 Mon Sep 17 00:00:00 2001 From: Humberto Yusta Date: Fri, 30 May 2025 18:17:26 +0200 Subject: [PATCH 15/50] fix: language typos --- refact-agent/engine/src/ast/treesitter/parsers/js.rs | 6 +++--- refact-agent/engine/src/ast/treesitter/parsers/tests/js.rs | 4 ++-- refact-agent/engine/src/ast/treesitter/parsers/tests/ts.rs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/refact-agent/engine/src/ast/treesitter/parsers/js.rs b/refact-agent/engine/src/ast/treesitter/parsers/js.rs index a2866ef26..b3482f677 100644 --- a/refact-agent/engine/src/ast/treesitter/parsers/js.rs +++ b/refact-agent/engine/src/ast/treesitter/parsers/js.rs @@ -477,7 +477,7 @@ impl JSParser { "identifier" /*| "field_identifier"*/ => { let mut usage = VariableUsage::default(); usage.ast_fields.file_path = path.clone(); - usage.ast_fields.language = LanguageId::TypeScript; + usage.ast_fields.language = LanguageId::JavaScript; usage.ast_fields.is_error = true; usage.ast_fields.name = code.slice(parent.byte_range()).to_string(); usage.ast_fields.full_range = parent.range(); @@ -491,7 +491,7 @@ impl JSParser { "member_expression" => { let mut usage = VariableUsage::default(); usage.ast_fields.file_path = path.clone(); - usage.ast_fields.language = LanguageId::TypeScript; + usage.ast_fields.language = LanguageId::JavaScript; usage.ast_fields.is_error = true; if let Some(property) = parent.child_by_field_name("property") { usage.ast_fields.name = code.slice(property.byte_range()).to_string(); @@ -747,7 +747,7 @@ impl JSParser { let mut ast_fields = AstSymbolFields::default(); ast_fields.file_path = path.clone(); ast_fields.is_error = false; - ast_fields.language = LanguageId::TypeScript; + ast_fields.language = LanguageId::JavaScript; let mut candidates = VecDeque::from(vec![CandidateInfo { ast_fields, diff --git a/refact-agent/engine/src/ast/treesitter/parsers/tests/js.rs b/refact-agent/engine/src/ast/treesitter/parsers/tests/js.rs index a48b73028..a8d829388 100644 --- a/refact-agent/engine/src/ast/treesitter/parsers/tests/js.rs +++ b/refact-agent/engine/src/ast/treesitter/parsers/tests/js.rs @@ -28,7 +28,7 @@ mod tests { let file = canonicalize(PathBuf::from(file!())).unwrap().parent().unwrap().join("cases/js/car.js"); assert!(file.exists()); - base_skeletonizer_test(&LanguageId::Java, &mut parser, &file, CAR_JS_CODE, CAR_JS_SKELETON); + base_skeletonizer_test(&LanguageId::JavaScript, &mut parser, &file, CAR_JS_CODE, CAR_JS_SKELETON); } #[test] @@ -36,6 +36,6 @@ mod tests { let mut parser: Box = Box::new(JSParser::new().expect("JSParser::new")); let file = canonicalize(PathBuf::from(file!())).unwrap().parent().unwrap().join("cases/js/car.js"); assert!(file.exists()); - base_declaration_formatter_test(&LanguageId::Java, &mut parser, &file, CAR_JS_CODE, CAR_JS_DECLS); + base_declaration_formatter_test(&LanguageId::JavaScript, &mut parser, &file, CAR_JS_CODE, CAR_JS_DECLS); } } diff --git a/refact-agent/engine/src/ast/treesitter/parsers/tests/ts.rs b/refact-agent/engine/src/ast/treesitter/parsers/tests/ts.rs index 9109e6f24..b19421ebf 100644 --- a/refact-agent/engine/src/ast/treesitter/parsers/tests/ts.rs +++ b/refact-agent/engine/src/ast/treesitter/parsers/tests/ts.rs @@ -28,7 +28,7 @@ mod tests { let file = canonicalize(PathBuf::from(file!())).unwrap().parent().unwrap().join("cases/ts/person.ts"); assert!(file.exists()); - base_skeletonizer_test(&LanguageId::Java, &mut parser, &file, PERSON_TS_CODE, PERSON_TS_SKELETON); + base_skeletonizer_test(&LanguageId::TypeScript, &mut parser, &file, PERSON_TS_CODE, PERSON_TS_SKELETON); } #[test] @@ -36,6 +36,6 @@ mod tests { let mut parser: Box = Box::new(TSParser::new().expect("TSParser::new")); let file = canonicalize(PathBuf::from(file!())).unwrap().parent().unwrap().join("cases/ts/person.ts"); assert!(file.exists()); - base_declaration_formatter_test(&LanguageId::Java, &mut parser, &file, PERSON_TS_CODE, PERSON_TS_DECLS); + base_declaration_formatter_test(&LanguageId::TypeScript, &mut parser, &file, PERSON_TS_CODE, PERSON_TS_DECLS); } } From a7d5b5772aa46d5acc004c7268c5659fac4336f1 Mon Sep 17 00:00:00 2001 From: Humberto Yusta Date: Fri, 30 May 2025 18:30:28 +0200 Subject: [PATCH 16/50] fix: db env settings --- refact-agent/engine/src/ast/ast_db.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/refact-agent/engine/src/ast/ast_db.rs b/refact-agent/engine/src/ast/ast_db.rs index 8dda5c511..5cc8bf9cd 100644 --- a/refact-agent/engine/src/ast/ast_db.rs +++ b/refact-agent/engine/src/ast/ast_db.rs @@ -85,7 +85,9 @@ pub async fn ast_index_init(ast_permanent: String, ast_max_files: usize) -> Arc< tracing::info!("starting AST db, ast_permanent={:?}", ast_permanent); let db_env: Arc = Arc::new(task::spawn_blocking(move || { - let options = heed::EnvOpenOptions::new(); + let mut options = heed::EnvOpenOptions::new(); + options.map_size(10 * 1024 * 1024 * 1024); // 10GB, max DB size + options.max_dbs(10); unsafe { options.open(db_path).unwrap() } }).await.unwrap()); From 9dfdf5e5a9555dee21e25ee4eca0462d69de3fa1 Mon Sep 17 00:00:00 2001 From: Humberto Yusta Date: Mon, 2 Jun 2025 10:34:06 +0200 Subject: [PATCH 17/50] fix: rm mutex from at commands after conflict resolution --- .../engine/src/at_commands/at_commands.rs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/refact-agent/engine/src/at_commands/at_commands.rs b/refact-agent/engine/src/at_commands/at_commands.rs index c007a5c74..be18e864b 100644 --- a/refact-agent/engine/src/at_commands/at_commands.rs +++ b/refact-agent/engine/src/at_commands/at_commands.rs @@ -92,17 +92,17 @@ pub trait AtParam: Send + Sync { pub async fn at_commands_dict(gcx: Arc>) -> HashMap> { let at_commands_dict = HashMap::from([ - ("@file".to_string(), Arc::new(AMutex::new(Box::new(AtFile::new()) as Box))), - // ("@file-search".to_string(), Arc::new(AMutex::new(Box::new(AtFileSearch::new()) as Box))), - ("@definition".to_string(), Arc::new(AMutex::new(Box::new(AtAstDefinition::new()) as Box))), - ("@references".to_string(), Arc::new(AMutex::new(Box::new(AtAstReference::new()) as Box))), - // ("@local-notes-to-self".to_string(), Arc::new(AMutex::new(Box::new(AtLocalNotesToSelf::new()) as Box))), - ("@tree".to_string(), Arc::new(AMutex::new(Box::new(AtTree::new()) as Box))), - // ("@diff".to_string(), Arc::new(AMutex::new(Box::new(AtDiff::new()) as Box))), - // ("@diff-rev".to_string(), Arc::new(AMutex::new(Box::new(AtDiffRev::new()) as Box))), - ("@web".to_string(), Arc::new(AMutex::new(Box::new(AtWeb::new()) as Box))), - ("@search".to_string(), Arc::new(AMutex::new(Box::new(crate::at_commands::at_search::AtSearch::new()) as Box))), - ("@knowledge-load".to_string(), Arc::new(AMutex::new(Box::new(crate::at_commands::at_knowledge::AtLoadKnowledge::new()) as Box))), + ("@file".to_string(), Arc::new(AtFile::new()) as Arc), + // ("@file-search".to_string(), Arc::new(AtFileSearch::new()) as Arc), + ("@definition".to_string(), Arc::new(AtAstDefinition::new()) as Arc), + ("@references".to_string(), Arc::new(AtAstReference::new()) as Arc), + // ("@local-notes-to-self".to_string(), Arc::new(AtLocalNotesToSelf::new()) as Arc), + ("@tree".to_string(), Arc::new(AtTree::new()) as Arc), + // ("@diff".to_string(), Arc::new(AtDiff::new()) as Arc), + // ("@diff-rev".to_string(), Arc::new(AtDiffRev::new()) as Arc), + ("@web".to_string(), Arc::new(AtWeb::new()) as Arc), + ("@search".to_string(), Arc::new(crate::at_commands::at_search::AtSearch::new()) as Arc), + ("@knowledge-load".to_string(), Arc::new(crate::at_commands::at_knowledge::AtLoadKnowledge::new()) as Arc), ]); let (ast_on, vecdb_on) = { From b796e1246c61358b5c742563c5de544e44e47c43 Mon Sep 17 00:00:00 2001 From: Humberto Yusta Date: Wed, 4 Jun 2025 22:45:10 +0200 Subject: [PATCH 18/50] fix: small fixes from review --- refact-agent/engine/src/ast/ast_db.rs | 5 +++-- refact-agent/engine/src/at_commands/at_tree.rs | 7 +------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/refact-agent/engine/src/ast/ast_db.rs b/refact-agent/engine/src/ast/ast_db.rs index 5cc8bf9cd..e69b150cb 100644 --- a/refact-agent/engine/src/ast/ast_db.rs +++ b/refact-agent/engine/src/ast/ast_db.rs @@ -60,6 +60,7 @@ use crate::fuzzy_search::fuzzy_search; // Read tests below, the show what this index can do! +const MAX_DB_SIZE: usize = 10 * 1024 * 1024 * 1024; // 10GB const A_LOT_OF_PRINTS: bool = false; macro_rules! debug_print { @@ -86,7 +87,7 @@ pub async fn ast_index_init(ast_permanent: String, ast_max_files: usize) -> Arc< tracing::info!("starting AST db, ast_permanent={:?}", ast_permanent); let db_env: Arc = Arc::new(task::spawn_blocking(move || { let mut options = heed::EnvOpenOptions::new(); - options.map_size(10 * 1024 * 1024 * 1024); // 10GB, max DB size + options.map_size(MAX_DB_SIZE); options.max_dbs(10); unsafe { options.open(db_path).unwrap() } }).await.unwrap()); @@ -213,7 +214,7 @@ pub async fn doc_add( Ok((defs.into_iter().map(Arc::new).collect(), language)) } -pub fn doc_remove(ast_index: Arc, cpath: &String) +pub fn doc_remove(ast_index: Arc, cpath: &String) -> () { let file_global_path = filesystem_path_to_double_colon_path(cpath); let d_prefix = format!("d|{}::", file_global_path.join("::")); diff --git a/refact-agent/engine/src/at_commands/at_tree.rs b/refact-agent/engine/src/at_commands/at_tree.rs index 94deb8a73..5dc03c725 100644 --- a/refact-agent/engine/src/at_commands/at_tree.rs +++ b/refact-agent/engine/src/at_commands/at_tree.rs @@ -206,12 +206,7 @@ async fn _print_files_tree( let mut result = String::new(); for (name, node) in &tree.children { - let db_mb = if let Some(ast) = ast_db.clone() { - Some(ast) - } else { - None - }; - if let Some(output) = traverse(node, PathBuf::from(name), 0, maxdepth, db_mb.clone()) { + if let Some(output) = traverse(node, PathBuf::from(name), 0, maxdepth, ast_db.clone()) { result.push_str(&output); } else { break; From e9c8a5b66ed236f91f88c7d2425f906e049e886c Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Thu, 5 Jun 2025 19:20:24 +0200 Subject: [PATCH 19/50] feat: better wording & animated confirmation card & workspace name matching mechanism --- .../ConfirmGroupSelection.module.css | 2 +- .../ConfirmGroupSelection.tsx | 118 +++++++++++------- .../Sidebar/GroupTree/CustomTreeNode.tsx | 26 +++- .../Sidebar/GroupTree/GroupTree.tsx | 55 ++++---- 4 files changed, 129 insertions(+), 72 deletions(-) diff --git a/refact-agent/gui/src/components/Sidebar/GroupTree/ConfirmGroupSelection/ConfirmGroupSelection.module.css b/refact-agent/gui/src/components/Sidebar/GroupTree/ConfirmGroupSelection/ConfirmGroupSelection.module.css index c9721cece..014f7d9e1 100644 --- a/refact-agent/gui/src/components/Sidebar/GroupTree/ConfirmGroupSelection/ConfirmGroupSelection.module.css +++ b/refact-agent/gui/src/components/Sidebar/GroupTree/ConfirmGroupSelection/ConfirmGroupSelection.module.css @@ -5,7 +5,7 @@ /* max-width: 480px; */ margin-left: auto; margin-right: auto; - background: var(--color-panel, #181a20); + /* background: var(--color-panel-translucent, #181a20); */ min-height: fit-content; } diff --git a/refact-agent/gui/src/components/Sidebar/GroupTree/ConfirmGroupSelection/ConfirmGroupSelection.tsx b/refact-agent/gui/src/components/Sidebar/GroupTree/ConfirmGroupSelection/ConfirmGroupSelection.tsx index ce771831f..898a0ae06 100644 --- a/refact-agent/gui/src/components/Sidebar/GroupTree/ConfirmGroupSelection/ConfirmGroupSelection.tsx +++ b/refact-agent/gui/src/components/Sidebar/GroupTree/ConfirmGroupSelection/ConfirmGroupSelection.tsx @@ -1,6 +1,7 @@ +import { motion } from "framer-motion"; import { Button, Card, Checkbox, Flex, Heading, Text } from "@radix-ui/themes"; import { FlexusTreeNode } from "../GroupTree"; -import React, { useCallback, useState } from "react"; +import React, { useCallback, useMemo, useState } from "react"; import styles from "./ConfirmGroupSelection.module.css"; import { useMutation } from "urql"; @@ -34,8 +35,13 @@ export const ConfirmGroupSelection: React.FC = ({ >(CreateGroupDocument); const [createFolderChecked, setCreateFolderChecked] = useState(false); + + const isMatchingGroupNameWithWorkspace = useMemo(() => { + return currentSelectedTeamsGroupNode.treenodeTitle === currentWorkspaceName; + }, [currentSelectedTeamsGroupNode.treenodeTitle, currentWorkspaceName]); + const handleConfirmClick = useCallback(async () => { - if (createFolderChecked) { + if (createFolderChecked && !isMatchingGroupNameWithWorkspace) { const result = await createGroup({ fgroup_name: currentWorkspaceName, fgroup_parent_id: currentSelectedTeamsGroupNode.treenodeId, @@ -73,52 +79,74 @@ export const ConfirmGroupSelection: React.FC = ({ setCurrentSelectedTeamsGroupNode, onGroupSelectionConfirm, currentWorkspaceName, + isMatchingGroupNameWithWorkspace, ]); + const cardVariants = { + initial: { y: -30, scale: 0.95, opacity: 0 }, + animate: { y: 0, scale: 1, opacity: 1 }, + exit: { scale: 0.95, opacity: 0 }, + }; + return ( - - - - Do you want to attach your current workspace to the  - - {currentSelectedTeamsGroupNode.treenodeTitle} - -  group? - - - This will help you sync your workspace with the selected group in the - cloud. - - - - setCreateFolderChecked(checked) - } - /> - - - - - + + + + + Would you like to connect your current workspace in IDE to the  + + {currentSelectedTeamsGroupNode.treenodeTitle} + +  group? + + + This will help you synchronize your local workspace with the + selected group in the cloud. + + {!isMatchingGroupNameWithWorkspace && ( + + + setCreateFolderChecked(checked) + } + /> + + + )} + + + + - - + + ); }; diff --git a/refact-agent/gui/src/components/Sidebar/GroupTree/CustomTreeNode.tsx b/refact-agent/gui/src/components/Sidebar/GroupTree/CustomTreeNode.tsx index 7fc8ba6e1..d86f95694 100644 --- a/refact-agent/gui/src/components/Sidebar/GroupTree/CustomTreeNode.tsx +++ b/refact-agent/gui/src/components/Sidebar/GroupTree/CustomTreeNode.tsx @@ -1,12 +1,18 @@ -import React, { useCallback } from "react"; -import { Box, Flex, Text } from "@radix-ui/themes"; -import { ChevronDownIcon, ChevronRightIcon } from "@radix-ui/react-icons"; +import React, { useCallback, useMemo } from "react"; +import { Box, Flex, Text, Tooltip } from "@radix-ui/themes"; +import { + BookmarkFilledIcon, + ChevronDownIcon, + ChevronRightIcon, +} from "@radix-ui/react-icons"; import type { NodeRendererProps } from "react-arborist"; import { FolderIcon } from "./FolderIcon"; import styles from "./CustomTreeNode.module.css"; import { TeamsGroup } from "../../../services/smallcloud/types"; import { FlexusTreeNode } from "./GroupTree"; +import { useAppSelector } from "../../../hooks"; +import { selectConfig } from "../../../features/Config/configSlice"; export type TeamsGroupTree = TeamsGroup & { children?: TeamsGroup[]; @@ -17,6 +23,9 @@ export const CustomTreeNode = ({ style, dragHandle, }: NodeRendererProps & { updateTree: (newTree: T[]) => void }) => { + const currentWorkspaceName = + useAppSelector(selectConfig).currentWorkspaceName ?? "New Project"; + // Determine if this is a folder (has children) const isContainingChildren = node.data.treenodeChildren.length > 0; @@ -67,6 +76,10 @@ export const CustomTreeNode = ({ return ; }; + const isMatchingWorkspaceNameInIDE = useMemo(() => { + return node.data.treenodeTitle === currentWorkspaceName; + }, [node.data.treenodeTitle, currentWorkspaceName]); + return ( ({ > {node.data.treenodeTitle} + {isMatchingWorkspaceNameInIDE && ( + + + + )} ); }; diff --git a/refact-agent/gui/src/components/Sidebar/GroupTree/GroupTree.tsx b/refact-agent/gui/src/components/Sidebar/GroupTree/GroupTree.tsx index 4a1d42473..6f6f4446d 100644 --- a/refact-agent/gui/src/components/Sidebar/GroupTree/GroupTree.tsx +++ b/refact-agent/gui/src/components/Sidebar/GroupTree/GroupTree.tsx @@ -1,4 +1,4 @@ -import { Flex, Heading, Select, Text } from "@radix-ui/themes"; +import { Flex, Heading, Select, Separator, Text } from "@radix-ui/themes"; import React from "react"; import { Tree } from "react-arborist"; import { CustomTreeNode } from "./CustomTreeNode"; @@ -7,6 +7,7 @@ import styles from "./GroupTree.module.css"; import { ConfirmGroupSelection } from "./ConfirmGroupSelection"; import { useGroupTree } from "./useGroupTree"; import { ScrollArea } from "../../ScrollArea"; +import { AnimatePresence } from "framer-motion"; export interface FlexusTreeNode { treenodePath: string; @@ -35,19 +36,24 @@ export const GroupTree: React.FC = () => { } = useGroupTree(); return ( - + - Choose workspace + Refact Teams Wizard - - Select a workspace associated to your team to continue. + + + Source selection + + + Select a cloud-based source associated with your team account to + continue. - + {availableWorkspaces.map((workspace) => ( @@ -58,7 +64,7 @@ export const GroupTree: React.FC = () => { {availableWorkspaces.length === 0 && ( - No workspaces are currently associated with your account. Please + No sources are currently associated with your team account. Please contact your Team Workspace administrator to request access. For further assistance, please refer to the support or bug reporting channels. @@ -72,14 +78,16 @@ export const GroupTree: React.FC = () => { width="100%" height="100%" justify="between" - style={{ flex: 1, minHeight: 0 }} // <-- Add this line + style={{ flex: 1, minHeight: 0 }} > - - Choose desired group + + Group selection - - Select a group to sync your knowledge with the cloud. + + Choose a group within the selected source where your local + knowledge, chats, and features will be uploaded and synchronized + in the cloud. { width="100%" indent={28} onSelect={onGroupSelect} - openByDefault={false} + openByDefault={true} className={styles.sidebarTree} selection={currentSelectedTeamsGroupNode?.treenodePath} disableDrag @@ -109,16 +117,17 @@ export const GroupTree: React.FC = () => { )} - {/* TODO: make it wrapped around AnimatePresence from motion */} - {currentSelectedTeamsGroupNode !== null && ( - - )} + + {currentSelectedTeamsGroupNode !== null && ( + + )} + )} From b090a894096abf8126bdd5c082c83bf0f9f19a16 Mon Sep 17 00:00:00 2001 From: Dimitry Ageev Date: Fri, 6 Jun 2025 08:45:21 +0200 Subject: [PATCH 20/50] tools improvements pack (#813) * tools improvements pack * diff in cloud --- refact-agent/engine/src/cloud/messages_req.rs | 12 ++++++++---- .../src/scratchpads/passthrough_convert_messages.rs | 12 ++++++++---- refact-agent/engine/src/tools/file_edit/auxiliary.rs | 4 ++-- .../src/tools/file_edit/tool_update_textdoc.rs | 4 ++-- .../src/tools/file_edit/tool_update_textdoc_regex.rs | 4 ++-- refact-agent/engine/src/tools/tool_regex_search.rs | 6 +++--- 6 files changed, 25 insertions(+), 17 deletions(-) diff --git a/refact-agent/engine/src/cloud/messages_req.rs b/refact-agent/engine/src/cloud/messages_req.rs index 7b83144a6..04f16ee30 100644 --- a/refact-agent/engine/src/cloud/messages_req.rs +++ b/refact-agent/engine/src/cloud/messages_req.rs @@ -289,10 +289,14 @@ pub fn convert_messages_to_thread_messages( } else if msg.role == "diff" { let extra_message = match serde_json::from_str::>(&msg.content.content_text_only()) { Ok(chunks) => { - chunks.iter() - .filter(|x| !x.application_details.is_empty()) - .map(|x| x.application_details.clone()) - .join("\n") + if chunks.is_empty() { + "Nothing has changed.".to_string() + } else { + chunks.iter() + .filter(|x| !x.application_details.is_empty()) + .map(|x| x.application_details.clone()) + .join("\n") + } }, Err(_) => "".to_string() }; diff --git a/refact-agent/engine/src/scratchpads/passthrough_convert_messages.rs b/refact-agent/engine/src/scratchpads/passthrough_convert_messages.rs index c45dbdfad..5f733cfff 100644 --- a/refact-agent/engine/src/scratchpads/passthrough_convert_messages.rs +++ b/refact-agent/engine/src/scratchpads/passthrough_convert_messages.rs @@ -52,10 +52,14 @@ pub fn convert_messages_to_openai_format(messages: Vec, style: &Opt } else if msg.role == "diff" { let extra_message = match serde_json::from_str::>(&msg.content.content_text_only()) { Ok(chunks) => { - chunks.iter() - .filter(|x| !x.application_details.is_empty()) - .map(|x| x.application_details.clone()) - .join("\n") + if chunks.is_empty() { + "Nothing has changed.".to_string() + } else { + chunks.iter() + .filter(|x| !x.application_details.is_empty()) + .map(|x| x.application_details.clone()) + .join("\n") + } }, Err(_) => "".to_string() }; diff --git a/refact-agent/engine/src/tools/file_edit/auxiliary.rs b/refact-agent/engine/src/tools/file_edit/auxiliary.rs index 8dd6249c8..dbab3d4aa 100644 --- a/refact-agent/engine/src/tools/file_edit/auxiliary.rs +++ b/refact-agent/engine/src/tools/file_edit/auxiliary.rs @@ -192,7 +192,7 @@ pub async fn str_replace( let occurrences = normalized_content.matches(&normalized_old_str).count(); if occurrences == 0 { return Err(format!( - "No replacement was performed, `old_str` did not appear verbatim in {:?}. Consider checking the file content using `cat()`", + "No replacement was performed, `old_str` did not appear verbatim in {:?}. Check the file content using `cat()`", path )); } @@ -232,7 +232,7 @@ pub async fn str_replace_regex( let occurrences = matches.len(); if occurrences == 0 { return Err(format!( - "No replacement was performed, `pattern` did not appear verbatim in {:?}. Consider checking the file content using `cat()`", + "No replacement was performed, `pattern` did not appear verbatim in {:?}. Check the file content using `cat()`", path )); } diff --git a/refact-agent/engine/src/tools/file_edit/tool_update_textdoc.rs b/refact-agent/engine/src/tools/file_edit/tool_update_textdoc.rs index f7448d033..8dea2506c 100644 --- a/refact-agent/engine/src/tools/file_edit/tool_update_textdoc.rs +++ b/refact-agent/engine/src/tools/file_edit/tool_update_textdoc.rs @@ -76,7 +76,7 @@ async fn parse_args( } }, Some(v) => return Err(format!("Error: The 'multiple' argument must be a boolean (true/false) indicating whether to replace all occurrences, but received: {:?}", v)), - None => return Err("Error: The 'multiple' argument is required. Please specify true to replace all occurrences or false to replace only the first occurrence.".to_string()) + None => false, }; Ok(ToolUpdateTextDocArgs { @@ -210,7 +210,7 @@ impl Tool for ToolUpdateTextDoc { param_type: "boolean".to_string(), } ], - parameters_required: vec!["path".to_string(), "old_str".to_string(), "replacement".to_string(), "multiple".to_string()], + parameters_required: vec!["path".to_string(), "old_str".to_string(), "replacement".to_string()], } } } diff --git a/refact-agent/engine/src/tools/file_edit/tool_update_textdoc_regex.rs b/refact-agent/engine/src/tools/file_edit/tool_update_textdoc_regex.rs index 98ba5502e..f02be5a09 100644 --- a/refact-agent/engine/src/tools/file_edit/tool_update_textdoc_regex.rs +++ b/refact-agent/engine/src/tools/file_edit/tool_update_textdoc_regex.rs @@ -84,7 +84,7 @@ async fn parse_args( } }, Some(v) => return Err(format!("Error: The 'multiple' argument must be a boolean (true/false) indicating whether to replace all occurrences, but received: {:?}", v)), - None => return Err("Error: The 'multiple' argument is required. Please specify true to replace all occurrences or false to replace only the first occurrence.".to_string()) + None => false, }; Ok(ToolUpdateTextDocRegexArgs { @@ -218,7 +218,7 @@ impl Tool for ToolUpdateTextDocRegex { param_type: "boolean".to_string(), } ], - parameters_required: vec!["path".to_string(), "pattern".to_string(), "replacement".to_string(), "multiple".to_string()], + parameters_required: vec!["path".to_string(), "pattern".to_string(), "replacement".to_string()], } } } diff --git a/refact-agent/engine/src/tools/tool_regex_search.rs b/refact-agent/engine/src/tools/tool_regex_search.rs index 824458e52..609ae8030 100644 --- a/refact-agent/engine/src/tools/tool_regex_search.rs +++ b/refact-agent/engine/src/tools/tool_regex_search.rs @@ -158,7 +158,7 @@ async fn smart_compress_results( } if estimated_size > MAX_OUTPUT_SIZE { info!("Compressing `search_pattern` output: estimated {} bytes (exceeds 4KB limit)", estimated_size); - content.push_str("\nNote: Output has been compressed. Use more specific patterns or scopes for detailed results."); + content.push_str("\nNote: Output has been compressed. Use more specific pattern or scope for detailed results."); } content } @@ -177,7 +177,7 @@ impl Tool for ToolRegexSearch { }, agentic: false, experimental: false, - description: "Search for files and folders whose names or paths match the given regular expression pattern, and also search for text matches inside files using the same patterns. Reports both path matches and text matches in separate sections.".to_string(), + description: "Search for files and folders whose names or paths match the given regular expression pattern, and also search for text matches inside files using the same pattern. Reports both path matches and text matches in separate sections.".to_string(), parameters: vec![ ToolParam { name: "pattern".to_string(), @@ -282,7 +282,7 @@ impl Tool for ToolRegexSearch { } if all_search_results.is_empty() { - return Err("All pattern searches produced no results. Try adjusting your patterns or scope.".to_string()); + return Err("All pattern searches produced no results. Try adjusting your pattern or scope.".to_string()); } let mut results = vec_context_file_to_context_tools(all_search_results); From 1d718b9a54e18485e1245645918f7da19ff42a5a Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Fri, 6 Jun 2025 18:02:51 +0200 Subject: [PATCH 21/50] fix: wording & workspaces get graphql schema update --- refact-agent/gui/generated/documents.ts | 423 ++++++++++++++---- refact-agent/gui/generated/graphql/gql.ts | 6 +- refact-agent/gui/generated/graphql/graphql.ts | 339 +++++++++++--- refact-agent/gui/generated/schema.graphql | 189 +++++--- .../ConfirmGroupSelection.tsx | 13 +- .../Sidebar/GroupTree/GroupTree.tsx | 24 +- .../GroupTree/NavTreeWantWorkspaces.graphql | 13 +- .../Sidebar/GroupTree/useGroupTree.ts | 16 +- 8 files changed, 798 insertions(+), 225 deletions(-) diff --git a/refact-agent/gui/generated/documents.ts b/refact-agent/gui/generated/documents.ts index e1bc44c02..c8c3f3f83 100644 --- a/refact-agent/gui/generated/documents.ts +++ b/refact-agent/gui/generated/documents.ts @@ -17,16 +17,20 @@ export type Scalars = { JSON: { input: any; output: any; } }; +export type BasicStuffResult = { + __typename?: 'BasicStuffResult'; + fuser_id: Scalars['String']['output']; + workspaces: Array; +}; + export type FExpertInput = { fexp_allow_tools: Scalars['String']['input']; fexp_block_tools: Scalars['String']['input']; fexp_name: Scalars['String']['input']; fexp_python_kernel: Scalars['String']['input']; fexp_system_prompt: Scalars['String']['input']; - fexp_ver_major: Scalars['Int']['input']; - fexp_ver_minor: Scalars['Int']['input']; located_fgroup_id: Scalars['String']['input']; - owner_fuser_id: Scalars['String']['input']; + owner_fuser_id?: InputMaybe; owner_shared: Scalars['Boolean']['input']; }; @@ -37,10 +41,8 @@ export type FExpertOutput = { fexp_name: Scalars['String']['output']; fexp_python_kernel: Scalars['String']['output']; fexp_system_prompt: Scalars['String']['output']; - fexp_ver_major: Scalars['Int']['output']; - fexp_ver_minor: Scalars['Int']['output']; - located_fgroup_id: Scalars['String']['output']; - owner_fuser_id: Scalars['String']['output']; + located_fgroup_id?: Maybe; + owner_fuser_id?: Maybe; owner_shared: Scalars['Boolean']['output']; }; @@ -54,11 +56,11 @@ export type FExpertSubs = { news_action: Scalars['String']['output']; news_payload: FExpertOutput; news_payload_id: Scalars['String']['output']; - news_ws_id: Scalars['String']['output']; + news_pubsub: Scalars['String']['output']; }; export type FExternalDataSourceInput = { - eds_json: Scalars['JSON']['input']; + eds_json: Scalars['String']['input']; eds_name: Scalars['String']['input']; eds_type: Scalars['String']['input']; located_fgroup_id: Scalars['String']['input']; @@ -80,7 +82,7 @@ export type FExternalDataSourceOutput = { }; export type FExternalDataSourcePatch = { - eds_json?: InputMaybe; + eds_json: Scalars['String']['input']; eds_last_successful_scan_ts?: InputMaybe; eds_name?: InputMaybe; eds_scan_status?: InputMaybe; @@ -97,6 +99,7 @@ export type FExternalDataSourceSubs = { }; export type FKnowledgeItemInput = { + iknow_is_core?: Scalars['Boolean']['input']; iknow_memory: Scalars['String']['input']; iknow_tags?: Array; located_fgroup_id: Scalars['String']['input']; @@ -107,6 +110,7 @@ export type FKnowledgeItemOutput = { __typename?: 'FKnowledgeItemOutput'; iknow_created_ts: Scalars['Float']['output']; iknow_id: Scalars['String']['output']; + iknow_is_core: Scalars['Boolean']['output']; iknow_memory: Scalars['String']['output']; iknow_modified_ts: Scalars['Float']['output']; iknow_stat_correct: Scalars['Int']['output']; @@ -119,6 +123,7 @@ export type FKnowledgeItemOutput = { }; export type FKnowledgeItemPatch = { + iknow_is_core?: InputMaybe; iknow_memory?: InputMaybe; iknow_tags?: InputMaybe>; located_fgroup_id?: InputMaybe; @@ -130,7 +135,31 @@ export type FKnowledgeItemSubs = { news_action: Scalars['String']['output']; news_payload?: Maybe; news_payload_id: Scalars['String']['output']; - news_ws_id: Scalars['String']['output']; + news_pubsub: Scalars['String']['output']; +}; + +export type FPermissionInput = { + fgroup_id: Scalars['String']['input']; + fuser_id: Scalars['String']['input']; + perm_role: Scalars['String']['input']; +}; + +export type FPermissionOutput = { + __typename?: 'FPermissionOutput'; + fgroup_id: Scalars['String']['output']; + fuser_id: Scalars['String']['output']; + perm_role: Scalars['String']['output']; +}; + +export type FPermissionPatch = { + perm_role?: InputMaybe; +}; + +export type FPluginOutput = { + __typename?: 'FPluginOutput'; + plugin_name: Scalars['String']['output']; + plugin_setup_page: Scalars['String']['output']; + plugin_version: Scalars['String']['output']; }; export type FThreadDelta = { @@ -140,44 +169,51 @@ export type FThreadDelta = { }; export type FThreadInput = { - ft_belongs_to_fce_id?: InputMaybe; + ft_app_capture?: Scalars['String']['input']; + ft_app_searchable?: Scalars['String']['input']; + ft_app_specific?: Scalars['String']['input']; + ft_error?: Scalars['String']['input']; ft_fexp_name: Scalars['String']['input']; - ft_fexp_ver_major: Scalars['Int']['input']; - ft_fexp_ver_minor: Scalars['Int']['input']; - ft_max_new_tokens?: Scalars['Int']['input']; - ft_model?: Scalars['String']['input']; - ft_n?: Scalars['Int']['input']; - ft_temperature?: Scalars['Float']['input']; ft_title: Scalars['String']['input']; + ft_toolset?: Scalars['String']['input']; located_fgroup_id: Scalars['String']['input']; owner_shared: Scalars['Boolean']['input']; + parent_ft_id?: InputMaybe; +}; + +export type FThreadMessageInput = { + ftm_alt: Scalars['Int']['input']; + ftm_app_specific?: InputMaybe; + ftm_belongs_to_ft_id: Scalars['String']['input']; + ftm_call_id: Scalars['String']['input']; + ftm_content?: InputMaybe; + ftm_num: Scalars['Int']['input']; + ftm_prev_alt: Scalars['Int']['input']; + ftm_provenance: Scalars['String']['input']; + ftm_role: Scalars['String']['input']; + ftm_tool_calls?: InputMaybe; + ftm_usage?: InputMaybe; + ftm_user_preferences?: InputMaybe; }; -export type FThreadMessage = { - __typename?: 'FThreadMessage'; +export type FThreadMessageOutput = { + __typename?: 'FThreadMessageOutput'; + ft_app_capture?: Maybe; + ft_app_searchable?: Maybe; + ft_app_specific?: Maybe; ftm_alt: Scalars['Int']['output']; + ftm_app_specific?: Maybe; ftm_belongs_to_ft_id: Scalars['String']['output']; ftm_call_id: Scalars['String']['output']; - ftm_content: Scalars['JSON']['output']; + ftm_content?: Maybe; ftm_created_ts: Scalars['Float']['output']; ftm_num: Scalars['Int']['output']; ftm_prev_alt: Scalars['Int']['output']; + ftm_provenance: Scalars['JSON']['output']; ftm_role: Scalars['String']['output']; ftm_tool_calls?: Maybe; ftm_usage?: Maybe; -}; - -export type FThreadMessageInput = { - ftm_alt: Scalars['Int']['input']; - ftm_belongs_to_ft_id: Scalars['String']['input']; - ftm_call_id: Scalars['String']['input']; - ftm_content: Scalars['String']['input']; - ftm_num: Scalars['Int']['input']; - ftm_prev_alt: Scalars['Int']['input']; - ftm_provenance: Scalars['String']['input']; - ftm_role: Scalars['String']['input']; - ftm_tool_calls: Scalars['String']['input']; - ftm_usage: Scalars['String']['input']; + ftm_user_preferences?: Maybe; }; export type FThreadMessageSubs = { @@ -185,14 +221,14 @@ export type FThreadMessageSubs = { news_action: Scalars['String']['output']; news_payload_id: Scalars['String']['output']; news_payload_thread?: Maybe; - news_payload_thread_message?: Maybe; + news_payload_thread_message?: Maybe; stream_delta?: Maybe; }; export type FThreadMessagesCreateResult = { __typename?: 'FThreadMessagesCreateResult'; count: Scalars['Int']['output']; - messages: Array; + messages: Array; }; export type FThreadMultipleMessagesInput = { @@ -202,43 +238,39 @@ export type FThreadMultipleMessagesInput = { export type FThreadOutput = { __typename?: 'FThreadOutput'; - ft_anything_new: Scalars['Boolean']['output']; + ft_app_capture: Scalars['String']['output']; + ft_app_searchable: Scalars['String']['output']; + ft_app_specific?: Maybe; ft_archived_ts: Scalars['Float']['output']; - ft_belongs_to_fce_id?: Maybe; ft_created_ts: Scalars['Float']['output']; - ft_error: Scalars['String']['output']; + ft_error?: Maybe; ft_fexp_name: Scalars['String']['output']; - ft_fexp_ver_major: Scalars['Int']['output']; - ft_fexp_ver_minor: Scalars['Int']['output']; ft_id: Scalars['String']['output']; ft_locked_by: Scalars['String']['output']; - ft_max_new_tokens: Scalars['Int']['output']; - ft_model: Scalars['String']['output']; - ft_n: Scalars['Int']['output']; ft_need_assistant: Scalars['Int']['output']; + ft_need_kernel: Scalars['Int']['output']; ft_need_tool_calls: Scalars['Int']['output']; - ft_temperature: Scalars['Float']['output']; + ft_need_user: Scalars['Int']['output']; ft_title: Scalars['String']['output']; - ft_toolset: Scalars['String']['output']; + ft_toolset?: Maybe; ft_updated_ts: Scalars['Float']['output']; located_fgroup_id: Scalars['String']['output']; owner_fuser_id: Scalars['String']['output']; owner_shared: Scalars['Boolean']['output']; + parent_ft_id?: Maybe; }; export type FThreadPatch = { - ft_anything_new?: InputMaybe; + ft_app_searchable?: InputMaybe; + ft_app_specific?: InputMaybe; ft_archived_ts?: InputMaybe; - ft_belongs_to_fce_id?: InputMaybe; ft_error?: InputMaybe; - ft_max_new_tokens?: InputMaybe; - ft_model?: InputMaybe; - ft_n?: InputMaybe; - ft_temperature?: InputMaybe; + ft_need_user?: InputMaybe; ft_title?: InputMaybe; ft_toolset?: InputMaybe; located_fgroup_id?: InputMaybe; owner_shared?: InputMaybe; + parent_ft_id?: InputMaybe; }; export type FThreadSubs = { @@ -246,7 +278,7 @@ export type FThreadSubs = { news_action: Scalars['String']['output']; news_payload?: Maybe; news_payload_id: Scalars['String']['output']; - news_ws_id: Scalars['String']['output']; + news_pubsub: Scalars['String']['output']; }; export type FWorkspace = { @@ -259,6 +291,27 @@ export type FWorkspace = { ws_status: Scalars['String']['output']; }; +export type FWorkspaceInvitationInput = { + ws_id: Scalars['String']['input']; + wsi_email: Scalars['String']['input']; + wsi_invited_by_fuser_id: Scalars['String']['input']; + wsi_role: Scalars['String']['input']; +}; + +export type FWorkspaceInvitationOutput = { + __typename?: 'FWorkspaceInvitationOutput'; + ws_id: Scalars['String']['output']; + wsi_created_ts: Scalars['Float']['output']; + wsi_email: Scalars['String']['output']; + wsi_invited_by_fuser_id: Scalars['String']['output']; + wsi_role: Scalars['String']['output']; + wsi_token: Scalars['String']['output']; +}; + +export type FWorkspaceInvitationPatch = { + wsi_role?: InputMaybe; +}; + export type FlexusGroup = { __typename?: 'FlexusGroup'; fgroup_created_ts: Scalars['Float']['output']; @@ -293,13 +346,24 @@ export type Mutation = { knowledge_item_delete: Scalars['Boolean']['output']; knowledge_item_mass_group_patch: Scalars['Int']['output']; knowledge_item_patch: FKnowledgeItemOutput; + permission_create: FPermissionOutput; + permission_delete: Scalars['Boolean']['output']; + permission_patch: FPermissionOutput; stats_add: Scalars['Boolean']['output']; + tech_support_activate: Scalars['Boolean']['output']; + tech_support_set_config: Scalars['Boolean']['output']; thread_create: FThreadOutput; thread_delete: Scalars['Boolean']['output']; + thread_lock: Scalars['Boolean']['output']; thread_mass_group_patch: Scalars['Int']['output']; - thread_message_create: FThreadMessage; + thread_message_create: FThreadMessageOutput; thread_messages_create_multiple: FThreadMessagesCreateResult; thread_patch: FThreadOutput; + thread_provide_toolset: Scalars['Boolean']['output']; + thread_unlock: Scalars['Boolean']['output']; + workspace_invitation_create: FWorkspaceInvitationOutput; + workspace_invitation_delete: Scalars['Boolean']['output']; + workspace_invitation_patch: FWorkspaceInvitationOutput; }; @@ -373,6 +437,24 @@ export type MutationKnowledge_Item_PatchArgs = { }; +export type MutationPermission_CreateArgs = { + input: FPermissionInput; +}; + + +export type MutationPermission_DeleteArgs = { + fgroup_id: Scalars['String']['input']; + fuser_id: Scalars['String']['input']; +}; + + +export type MutationPermission_PatchArgs = { + fgroup_id: Scalars['String']['input']; + fuser_id: Scalars['String']['input']; + patch: FPermissionPatch; +}; + + export type MutationStats_AddArgs = { st_how_many: Scalars['Int']['input']; st_involved_expert?: Scalars['String']['input']; @@ -384,6 +466,17 @@ export type MutationStats_AddArgs = { }; +export type MutationTech_Support_ActivateArgs = { + ws_id: Scalars['String']['input']; +}; + + +export type MutationTech_Support_Set_ConfigArgs = { + config: TechSupportSettingsInput; + ws_id: Scalars['String']['input']; +}; + + export type MutationThread_CreateArgs = { input: FThreadInput; }; @@ -394,6 +487,12 @@ export type MutationThread_DeleteArgs = { }; +export type MutationThread_LockArgs = { + ft_id: Scalars['String']['input']; + worker_name: Scalars['String']['input']; +}; + + export type MutationThread_Mass_Group_PatchArgs = { dst_group_id: Scalars['String']['input']; src_group_id: Scalars['String']['input']; @@ -415,14 +514,62 @@ export type MutationThread_PatchArgs = { patch: FThreadPatch; }; + +export type MutationThread_Provide_ToolsetArgs = { + ft_id: Scalars['String']['input']; + toolset: Scalars['String']['input']; +}; + + +export type MutationThread_UnlockArgs = { + ft_id: Scalars['String']['input']; + worker_name: Scalars['String']['input']; +}; + + +export type MutationWorkspace_Invitation_CreateArgs = { + input: FWorkspaceInvitationInput; +}; + + +export type MutationWorkspace_Invitation_DeleteArgs = { + ws_id: Scalars['String']['input']; + wsi_email: Scalars['String']['input']; +}; + + +export type MutationWorkspace_Invitation_PatchArgs = { + patch: FWorkspaceInvitationPatch; + ws_id: Scalars['String']['input']; + wsi_email: Scalars['String']['input']; +}; + export type Query = { __typename?: 'Query'; + expert_get: FExpertOutput; expert_list: Array; + experts_effective_list: Array; + external_data_source_get: FExternalDataSourceOutput; external_data_source_list: Array; + knowledge_item_get: FKnowledgeItemOutput; knowledge_item_list: Array; - login: Array; + permission_get: FPermissionOutput; + permission_list: Array; + plugins_installed: Array; + query_basic_stuff: BasicStuffResult; + tech_support_get_config?: Maybe; + thread_get: FThreadOutput; thread_list: Array; - thread_messages_list: Array; + thread_messages_list: Array; + threads_app_captured: Array; + workspace_invitation_get: FWorkspaceInvitationOutput; + workspace_invitation_list: Array; + workspace_permission_list: Array; +}; + + +export type QueryExpert_GetArgs = { + id: Scalars['String']['input']; }; @@ -434,6 +581,16 @@ export type QueryExpert_ListArgs = { }; +export type QueryExperts_Effective_ListArgs = { + located_fgroup_id: Scalars['String']['input']; +}; + + +export type QueryExternal_Data_Source_GetArgs = { + id: Scalars['String']['input']; +}; + + export type QueryExternal_Data_Source_ListArgs = { limit: Scalars['Int']['input']; located_fgroup_id: Scalars['String']['input']; @@ -442,6 +599,11 @@ export type QueryExternal_Data_Source_ListArgs = { }; +export type QueryKnowledge_Item_GetArgs = { + id: Scalars['String']['input']; +}; + + export type QueryKnowledge_Item_ListArgs = { limit: Scalars['Int']['input']; located_fgroup_id: Scalars['String']['input']; @@ -450,6 +612,27 @@ export type QueryKnowledge_Item_ListArgs = { }; +export type QueryPermission_GetArgs = { + fgroup_id: Scalars['String']['input']; + fuser_id: Scalars['String']['input']; +}; + + +export type QueryPermission_ListArgs = { + fgroup_id: Scalars['String']['input']; +}; + + +export type QueryTech_Support_Get_ConfigArgs = { + ws_id: Scalars['String']['input']; +}; + + +export type QueryThread_GetArgs = { + id: Scalars['String']['input']; +}; + + export type QueryThread_ListArgs = { limit: Scalars['Int']['input']; located_fgroup_id: Scalars['String']['input']; @@ -463,6 +646,29 @@ export type QueryThread_Messages_ListArgs = { ftm_alt?: InputMaybe; }; + +export type QueryThreads_App_CapturedArgs = { + ft_app_capture: Scalars['String']['input']; + ft_app_searchable: Scalars['String']['input']; + located_fgroup_id: Scalars['String']['input']; +}; + + +export type QueryWorkspace_Invitation_GetArgs = { + ws_id: Scalars['String']['input']; + wsi_email: Scalars['String']['input']; +}; + + +export type QueryWorkspace_Invitation_ListArgs = { + ws_id: Scalars['String']['input']; +}; + + +export type QueryWorkspace_Permission_ListArgs = { + ws_id: Scalars['String']['input']; +}; + export type Subscription = { __typename?: 'Subscription'; comprehensive_thread_subs: FThreadMessageSubs; @@ -512,6 +718,23 @@ export type SubscriptionTree_SubscriptionArgs = { ws_id: Scalars['String']['input']; }; +export type TechSupportSettingsInput = { + support_api_key: Scalars['String']['input']; + support_channel_list: Array; + support_discord_key: Scalars['String']['input']; + support_fgroup_id: Scalars['String']['input']; + support_fuser_id: Scalars['String']['input']; +}; + +export type TechSupportSettingsOutput = { + __typename?: 'TechSupportSettingsOutput'; + support_api_key: Scalars['String']['output']; + support_channel_list: Array; + support_discord_key: Scalars['String']['output']; + support_fgroup_id: Scalars['String']['output']; + support_fuser_id: Scalars['String']['output']; +}; + export type TreeUpdateSubs = { __typename?: 'TreeUpdateSubs'; treeupd_action: Scalars['String']['output']; @@ -539,12 +762,12 @@ export type NavTreeSubsSubscription = { __typename?: 'Subscription', tree_subscr export type NavTreeWantWorkspacesQueryVariables = Exact<{ [key: string]: never; }>; -export type NavTreeWantWorkspacesQuery = { __typename?: 'Query', login: Array<{ __typename?: 'FWorkspace', ws_id: string, ws_owner_fuser_id: string, ws_root_group_id: string, root_group_name: string }> }; +export type NavTreeWantWorkspacesQuery = { __typename?: 'Query', query_basic_stuff: { __typename?: 'BasicStuffResult', fuser_id: string, workspaces: Array<{ __typename?: 'FWorkspace', ws_id: string, ws_owner_fuser_id: string, ws_root_group_id: string, root_group_name: string }> } }; export const CreateGroupDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateGroup"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"fgroup_name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"fgroup_parent_id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"group_create"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"fgroup_name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"fgroup_name"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"fgroup_parent_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"fgroup_parent_id"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fgroup_id"}},{"kind":"Field","name":{"kind":"Name","value":"fgroup_name"}},{"kind":"Field","name":{"kind":"Name","value":"ws_id"}},{"kind":"Field","name":{"kind":"Name","value":"fgroup_parent_id"}},{"kind":"Field","name":{"kind":"Name","value":"fgroup_created_ts"}}]}}]}}]} as unknown as DocumentNode; export const NavTreeSubsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"NavTreeSubs"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"ws_id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"tree_subscription"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"ws_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"ws_id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"treeupd_action"}},{"kind":"Field","name":{"kind":"Name","value":"treeupd_id"}},{"kind":"Field","name":{"kind":"Name","value":"treeupd_path"}},{"kind":"Field","name":{"kind":"Name","value":"treeupd_type"}},{"kind":"Field","name":{"kind":"Name","value":"treeupd_title"}}]}}]}}]} as unknown as DocumentNode; -export const NavTreeWantWorkspacesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"NavTreeWantWorkspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ws_id"}},{"kind":"Field","name":{"kind":"Name","value":"ws_owner_fuser_id"}},{"kind":"Field","name":{"kind":"Name","value":"ws_root_group_id"}},{"kind":"Field","name":{"kind":"Name","value":"root_group_name"}}]}}]}}]} as unknown as DocumentNode; +export const NavTreeWantWorkspacesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"NavTreeWantWorkspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"query_basic_stuff"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fuser_id"}},{"kind":"Field","name":{"kind":"Name","value":"workspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ws_id"}},{"kind":"Field","name":{"kind":"Name","value":"ws_owner_fuser_id"}},{"kind":"Field","name":{"kind":"Name","value":"ws_root_group_id"}},{"kind":"Field","name":{"kind":"Name","value":"root_group_name"}}]}}]}}]}}]} as unknown as DocumentNode; type Properties = Required<{ [K in keyof T]: z.ZodType; @@ -563,10 +786,8 @@ export function FExpertInputSchema(): z.ZodObject> { fexp_name: z.string(), fexp_python_kernel: z.string(), fexp_system_prompt: z.string(), - fexp_ver_major: z.number(), - fexp_ver_minor: z.number(), located_fgroup_id: z.string(), - owner_fuser_id: z.string(), + owner_fuser_id: z.string().nullish(), owner_shared: z.boolean() }) } @@ -580,7 +801,7 @@ export function FExpertPatchSchema(): z.ZodObject> { export function FExternalDataSourceInputSchema(): z.ZodObject> { return z.object({ - eds_json: definedNonNullAnySchema, + eds_json: z.string(), eds_name: z.string(), eds_type: z.string(), located_fgroup_id: z.string() @@ -589,7 +810,7 @@ export function FExternalDataSourceInputSchema(): z.ZodObject> { return z.object({ - eds_json: definedNonNullAnySchema.nullish(), + eds_json: z.string(), eds_last_successful_scan_ts: z.number().nullish(), eds_name: z.string().nullish(), eds_scan_status: z.string().nullish(), @@ -601,6 +822,7 @@ export function FExternalDataSourcePatchSchema(): z.ZodObject> { return z.object({ + iknow_is_core: z.boolean().default(false), iknow_memory: z.string(), iknow_tags: z.array(z.string()), located_fgroup_id: z.string(), @@ -610,6 +832,7 @@ export function FKnowledgeItemInputSchema(): z.ZodObject> { return z.object({ + iknow_is_core: z.boolean().nullish(), iknow_memory: z.string().nullish(), iknow_tags: z.array(z.string()).nullish(), located_fgroup_id: z.string().nullish(), @@ -617,34 +840,49 @@ export function FKnowledgeItemPatchSchema(): z.ZodObject> { + return z.object({ + fgroup_id: z.string(), + fuser_id: z.string(), + perm_role: z.string() + }) +} + +export function FPermissionPatchSchema(): z.ZodObject> { + return z.object({ + perm_role: z.string().nullish() + }) +} + export function FThreadInputSchema(): z.ZodObject> { return z.object({ - ft_belongs_to_fce_id: z.string().nullish(), + ft_app_capture: z.string().default(""), + ft_app_searchable: z.string().default(""), + ft_app_specific: z.string().default("null"), + ft_error: z.string().default("null"), ft_fexp_name: z.string(), - ft_fexp_ver_major: z.number(), - ft_fexp_ver_minor: z.number(), - ft_max_new_tokens: z.number().default(8192), - ft_model: z.string().default(""), - ft_n: z.number().default(1), - ft_temperature: z.number().default(0), ft_title: z.string(), + ft_toolset: z.string().default("null"), located_fgroup_id: z.string(), - owner_shared: z.boolean() + owner_shared: z.boolean(), + parent_ft_id: z.string().nullish() }) } export function FThreadMessageInputSchema(): z.ZodObject> { return z.object({ ftm_alt: z.number(), + ftm_app_specific: z.string().default("null").nullish(), ftm_belongs_to_ft_id: z.string(), ftm_call_id: z.string(), - ftm_content: z.string(), + ftm_content: z.string().default("null").nullish(), ftm_num: z.number(), ftm_prev_alt: z.number(), ftm_provenance: z.string(), ftm_role: z.string(), - ftm_tool_calls: z.string(), - ftm_usage: z.string() + ftm_tool_calls: z.string().default("null").nullish(), + ftm_usage: z.string().default("null").nullish(), + ftm_user_preferences: z.string().default("null").nullish() }) } @@ -657,18 +895,31 @@ export function FThreadMultipleMessagesInputSchema(): z.ZodObject> { return z.object({ - ft_anything_new: z.boolean().nullish(), + ft_app_searchable: z.string().nullish(), + ft_app_specific: z.string().nullish(), ft_archived_ts: z.number().nullish(), - ft_belongs_to_fce_id: z.string().nullish(), ft_error: z.string().nullish(), - ft_max_new_tokens: z.number().nullish(), - ft_model: z.string().nullish(), - ft_n: z.number().nullish(), - ft_temperature: z.number().nullish(), + ft_need_user: z.number().nullish(), ft_title: z.string().nullish(), ft_toolset: z.string().nullish(), located_fgroup_id: z.string().nullish(), - owner_shared: z.boolean().nullish() + owner_shared: z.boolean().nullish(), + parent_ft_id: z.string().nullish() + }) +} + +export function FWorkspaceInvitationInputSchema(): z.ZodObject> { + return z.object({ + ws_id: z.string(), + wsi_email: z.string(), + wsi_invited_by_fuser_id: z.string(), + wsi_role: z.string() + }) +} + +export function FWorkspaceInvitationPatchSchema(): z.ZodObject> { + return z.object({ + wsi_role: z.string().nullish() }) } @@ -685,3 +936,13 @@ export function FlexusGroupPatchSchema(): z.ZodObject> { + return z.object({ + support_api_key: z.string(), + support_channel_list: z.array(z.string()), + support_discord_key: z.string(), + support_fgroup_id: z.string(), + support_fuser_id: z.string() + }) +} diff --git a/refact-agent/gui/generated/graphql/gql.ts b/refact-agent/gui/generated/graphql/gql.ts index 4b8702625..8bd921f99 100644 --- a/refact-agent/gui/generated/graphql/gql.ts +++ b/refact-agent/gui/generated/graphql/gql.ts @@ -16,12 +16,12 @@ import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document- type Documents = { "mutation CreateGroup($fgroup_name: String!, $fgroup_parent_id: String!) {\n group_create(\n input: {fgroup_name: $fgroup_name, fgroup_parent_id: $fgroup_parent_id}\n ) {\n fgroup_id\n fgroup_name\n ws_id\n fgroup_parent_id\n fgroup_created_ts\n }\n}": typeof types.CreateGroupDocument, "subscription NavTreeSubs($ws_id: String!) {\n tree_subscription(ws_id: $ws_id) {\n treeupd_action\n treeupd_id\n treeupd_path\n treeupd_type\n treeupd_title\n }\n}": typeof types.NavTreeSubsDocument, - "query NavTreeWantWorkspaces {\n login {\n ws_id\n ws_owner_fuser_id\n ws_root_group_id\n root_group_name\n }\n}": typeof types.NavTreeWantWorkspacesDocument, + "query NavTreeWantWorkspaces {\n query_basic_stuff {\n fuser_id\n workspaces {\n ws_id\n ws_owner_fuser_id\n ws_root_group_id\n root_group_name\n }\n }\n}": typeof types.NavTreeWantWorkspacesDocument, }; const documents: Documents = { "mutation CreateGroup($fgroup_name: String!, $fgroup_parent_id: String!) {\n group_create(\n input: {fgroup_name: $fgroup_name, fgroup_parent_id: $fgroup_parent_id}\n ) {\n fgroup_id\n fgroup_name\n ws_id\n fgroup_parent_id\n fgroup_created_ts\n }\n}": types.CreateGroupDocument, "subscription NavTreeSubs($ws_id: String!) {\n tree_subscription(ws_id: $ws_id) {\n treeupd_action\n treeupd_id\n treeupd_path\n treeupd_type\n treeupd_title\n }\n}": types.NavTreeSubsDocument, - "query NavTreeWantWorkspaces {\n login {\n ws_id\n ws_owner_fuser_id\n ws_root_group_id\n root_group_name\n }\n}": types.NavTreeWantWorkspacesDocument, + "query NavTreeWantWorkspaces {\n query_basic_stuff {\n fuser_id\n workspaces {\n ws_id\n ws_owner_fuser_id\n ws_root_group_id\n root_group_name\n }\n }\n}": types.NavTreeWantWorkspacesDocument, }; /** @@ -49,7 +49,7 @@ export function graphql(source: "subscription NavTreeSubs($ws_id: String!) {\n /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query NavTreeWantWorkspaces {\n login {\n ws_id\n ws_owner_fuser_id\n ws_root_group_id\n root_group_name\n }\n}"): (typeof documents)["query NavTreeWantWorkspaces {\n login {\n ws_id\n ws_owner_fuser_id\n ws_root_group_id\n root_group_name\n }\n}"]; +export function graphql(source: "query NavTreeWantWorkspaces {\n query_basic_stuff {\n fuser_id\n workspaces {\n ws_id\n ws_owner_fuser_id\n ws_root_group_id\n root_group_name\n }\n }\n}"): (typeof documents)["query NavTreeWantWorkspaces {\n query_basic_stuff {\n fuser_id\n workspaces {\n ws_id\n ws_owner_fuser_id\n ws_root_group_id\n root_group_name\n }\n }\n}"]; export function graphql(source: string) { return (documents as any)[source] ?? {}; diff --git a/refact-agent/gui/generated/graphql/graphql.ts b/refact-agent/gui/generated/graphql/graphql.ts index 94aa38c8a..ce5ae1718 100644 --- a/refact-agent/gui/generated/graphql/graphql.ts +++ b/refact-agent/gui/generated/graphql/graphql.ts @@ -18,16 +18,20 @@ export type Scalars = { JSON: { input: any; output: any; } }; +export type BasicStuffResult = { + __typename?: 'BasicStuffResult'; + fuser_id: Scalars['String']['output']; + workspaces: Array; +}; + export type FExpertInput = { fexp_allow_tools: Scalars['String']['input']; fexp_block_tools: Scalars['String']['input']; fexp_name: Scalars['String']['input']; fexp_python_kernel: Scalars['String']['input']; fexp_system_prompt: Scalars['String']['input']; - fexp_ver_major: Scalars['Int']['input']; - fexp_ver_minor: Scalars['Int']['input']; located_fgroup_id: Scalars['String']['input']; - owner_fuser_id: Scalars['String']['input']; + owner_fuser_id?: InputMaybe; owner_shared: Scalars['Boolean']['input']; }; @@ -38,10 +42,8 @@ export type FExpertOutput = { fexp_name: Scalars['String']['output']; fexp_python_kernel: Scalars['String']['output']; fexp_system_prompt: Scalars['String']['output']; - fexp_ver_major: Scalars['Int']['output']; - fexp_ver_minor: Scalars['Int']['output']; - located_fgroup_id: Scalars['String']['output']; - owner_fuser_id: Scalars['String']['output']; + located_fgroup_id?: Maybe; + owner_fuser_id?: Maybe; owner_shared: Scalars['Boolean']['output']; }; @@ -55,11 +57,11 @@ export type FExpertSubs = { news_action: Scalars['String']['output']; news_payload: FExpertOutput; news_payload_id: Scalars['String']['output']; - news_ws_id: Scalars['String']['output']; + news_pubsub: Scalars['String']['output']; }; export type FExternalDataSourceInput = { - eds_json: Scalars['JSON']['input']; + eds_json: Scalars['String']['input']; eds_name: Scalars['String']['input']; eds_type: Scalars['String']['input']; located_fgroup_id: Scalars['String']['input']; @@ -81,7 +83,7 @@ export type FExternalDataSourceOutput = { }; export type FExternalDataSourcePatch = { - eds_json?: InputMaybe; + eds_json: Scalars['String']['input']; eds_last_successful_scan_ts?: InputMaybe; eds_name?: InputMaybe; eds_scan_status?: InputMaybe; @@ -98,6 +100,7 @@ export type FExternalDataSourceSubs = { }; export type FKnowledgeItemInput = { + iknow_is_core?: Scalars['Boolean']['input']; iknow_memory: Scalars['String']['input']; iknow_tags?: Array; located_fgroup_id: Scalars['String']['input']; @@ -108,6 +111,7 @@ export type FKnowledgeItemOutput = { __typename?: 'FKnowledgeItemOutput'; iknow_created_ts: Scalars['Float']['output']; iknow_id: Scalars['String']['output']; + iknow_is_core: Scalars['Boolean']['output']; iknow_memory: Scalars['String']['output']; iknow_modified_ts: Scalars['Float']['output']; iknow_stat_correct: Scalars['Int']['output']; @@ -120,6 +124,7 @@ export type FKnowledgeItemOutput = { }; export type FKnowledgeItemPatch = { + iknow_is_core?: InputMaybe; iknow_memory?: InputMaybe; iknow_tags?: InputMaybe>; located_fgroup_id?: InputMaybe; @@ -131,7 +136,31 @@ export type FKnowledgeItemSubs = { news_action: Scalars['String']['output']; news_payload?: Maybe; news_payload_id: Scalars['String']['output']; - news_ws_id: Scalars['String']['output']; + news_pubsub: Scalars['String']['output']; +}; + +export type FPermissionInput = { + fgroup_id: Scalars['String']['input']; + fuser_id: Scalars['String']['input']; + perm_role: Scalars['String']['input']; +}; + +export type FPermissionOutput = { + __typename?: 'FPermissionOutput'; + fgroup_id: Scalars['String']['output']; + fuser_id: Scalars['String']['output']; + perm_role: Scalars['String']['output']; +}; + +export type FPermissionPatch = { + perm_role?: InputMaybe; +}; + +export type FPluginOutput = { + __typename?: 'FPluginOutput'; + plugin_name: Scalars['String']['output']; + plugin_setup_page: Scalars['String']['output']; + plugin_version: Scalars['String']['output']; }; export type FThreadDelta = { @@ -141,44 +170,51 @@ export type FThreadDelta = { }; export type FThreadInput = { - ft_belongs_to_fce_id?: InputMaybe; + ft_app_capture?: Scalars['String']['input']; + ft_app_searchable?: Scalars['String']['input']; + ft_app_specific?: Scalars['String']['input']; + ft_error?: Scalars['String']['input']; ft_fexp_name: Scalars['String']['input']; - ft_fexp_ver_major: Scalars['Int']['input']; - ft_fexp_ver_minor: Scalars['Int']['input']; - ft_max_new_tokens?: Scalars['Int']['input']; - ft_model?: Scalars['String']['input']; - ft_n?: Scalars['Int']['input']; - ft_temperature?: Scalars['Float']['input']; ft_title: Scalars['String']['input']; + ft_toolset?: Scalars['String']['input']; located_fgroup_id: Scalars['String']['input']; owner_shared: Scalars['Boolean']['input']; + parent_ft_id?: InputMaybe; }; -export type FThreadMessage = { - __typename?: 'FThreadMessage'; +export type FThreadMessageInput = { + ftm_alt: Scalars['Int']['input']; + ftm_app_specific?: InputMaybe; + ftm_belongs_to_ft_id: Scalars['String']['input']; + ftm_call_id: Scalars['String']['input']; + ftm_content?: InputMaybe; + ftm_num: Scalars['Int']['input']; + ftm_prev_alt: Scalars['Int']['input']; + ftm_provenance: Scalars['String']['input']; + ftm_role: Scalars['String']['input']; + ftm_tool_calls?: InputMaybe; + ftm_usage?: InputMaybe; + ftm_user_preferences?: InputMaybe; +}; + +export type FThreadMessageOutput = { + __typename?: 'FThreadMessageOutput'; + ft_app_capture?: Maybe; + ft_app_searchable?: Maybe; + ft_app_specific?: Maybe; ftm_alt: Scalars['Int']['output']; + ftm_app_specific?: Maybe; ftm_belongs_to_ft_id: Scalars['String']['output']; ftm_call_id: Scalars['String']['output']; - ftm_content: Scalars['JSON']['output']; + ftm_content?: Maybe; ftm_created_ts: Scalars['Float']['output']; ftm_num: Scalars['Int']['output']; ftm_prev_alt: Scalars['Int']['output']; + ftm_provenance: Scalars['JSON']['output']; ftm_role: Scalars['String']['output']; ftm_tool_calls?: Maybe; ftm_usage?: Maybe; -}; - -export type FThreadMessageInput = { - ftm_alt: Scalars['Int']['input']; - ftm_belongs_to_ft_id: Scalars['String']['input']; - ftm_call_id: Scalars['String']['input']; - ftm_content: Scalars['String']['input']; - ftm_num: Scalars['Int']['input']; - ftm_prev_alt: Scalars['Int']['input']; - ftm_provenance: Scalars['String']['input']; - ftm_role: Scalars['String']['input']; - ftm_tool_calls: Scalars['String']['input']; - ftm_usage: Scalars['String']['input']; + ftm_user_preferences?: Maybe; }; export type FThreadMessageSubs = { @@ -186,14 +222,14 @@ export type FThreadMessageSubs = { news_action: Scalars['String']['output']; news_payload_id: Scalars['String']['output']; news_payload_thread?: Maybe; - news_payload_thread_message?: Maybe; + news_payload_thread_message?: Maybe; stream_delta?: Maybe; }; export type FThreadMessagesCreateResult = { __typename?: 'FThreadMessagesCreateResult'; count: Scalars['Int']['output']; - messages: Array; + messages: Array; }; export type FThreadMultipleMessagesInput = { @@ -203,43 +239,39 @@ export type FThreadMultipleMessagesInput = { export type FThreadOutput = { __typename?: 'FThreadOutput'; - ft_anything_new: Scalars['Boolean']['output']; + ft_app_capture: Scalars['String']['output']; + ft_app_searchable: Scalars['String']['output']; + ft_app_specific?: Maybe; ft_archived_ts: Scalars['Float']['output']; - ft_belongs_to_fce_id?: Maybe; ft_created_ts: Scalars['Float']['output']; - ft_error: Scalars['String']['output']; + ft_error?: Maybe; ft_fexp_name: Scalars['String']['output']; - ft_fexp_ver_major: Scalars['Int']['output']; - ft_fexp_ver_minor: Scalars['Int']['output']; ft_id: Scalars['String']['output']; ft_locked_by: Scalars['String']['output']; - ft_max_new_tokens: Scalars['Int']['output']; - ft_model: Scalars['String']['output']; - ft_n: Scalars['Int']['output']; ft_need_assistant: Scalars['Int']['output']; + ft_need_kernel: Scalars['Int']['output']; ft_need_tool_calls: Scalars['Int']['output']; - ft_temperature: Scalars['Float']['output']; + ft_need_user: Scalars['Int']['output']; ft_title: Scalars['String']['output']; - ft_toolset: Scalars['String']['output']; + ft_toolset?: Maybe; ft_updated_ts: Scalars['Float']['output']; located_fgroup_id: Scalars['String']['output']; owner_fuser_id: Scalars['String']['output']; owner_shared: Scalars['Boolean']['output']; + parent_ft_id?: Maybe; }; export type FThreadPatch = { - ft_anything_new?: InputMaybe; + ft_app_searchable?: InputMaybe; + ft_app_specific?: InputMaybe; ft_archived_ts?: InputMaybe; - ft_belongs_to_fce_id?: InputMaybe; ft_error?: InputMaybe; - ft_max_new_tokens?: InputMaybe; - ft_model?: InputMaybe; - ft_n?: InputMaybe; - ft_temperature?: InputMaybe; + ft_need_user?: InputMaybe; ft_title?: InputMaybe; ft_toolset?: InputMaybe; located_fgroup_id?: InputMaybe; owner_shared?: InputMaybe; + parent_ft_id?: InputMaybe; }; export type FThreadSubs = { @@ -247,7 +279,7 @@ export type FThreadSubs = { news_action: Scalars['String']['output']; news_payload?: Maybe; news_payload_id: Scalars['String']['output']; - news_ws_id: Scalars['String']['output']; + news_pubsub: Scalars['String']['output']; }; export type FWorkspace = { @@ -260,6 +292,27 @@ export type FWorkspace = { ws_status: Scalars['String']['output']; }; +export type FWorkspaceInvitationInput = { + ws_id: Scalars['String']['input']; + wsi_email: Scalars['String']['input']; + wsi_invited_by_fuser_id: Scalars['String']['input']; + wsi_role: Scalars['String']['input']; +}; + +export type FWorkspaceInvitationOutput = { + __typename?: 'FWorkspaceInvitationOutput'; + ws_id: Scalars['String']['output']; + wsi_created_ts: Scalars['Float']['output']; + wsi_email: Scalars['String']['output']; + wsi_invited_by_fuser_id: Scalars['String']['output']; + wsi_role: Scalars['String']['output']; + wsi_token: Scalars['String']['output']; +}; + +export type FWorkspaceInvitationPatch = { + wsi_role?: InputMaybe; +}; + export type FlexusGroup = { __typename?: 'FlexusGroup'; fgroup_created_ts: Scalars['Float']['output']; @@ -294,13 +347,24 @@ export type Mutation = { knowledge_item_delete: Scalars['Boolean']['output']; knowledge_item_mass_group_patch: Scalars['Int']['output']; knowledge_item_patch: FKnowledgeItemOutput; + permission_create: FPermissionOutput; + permission_delete: Scalars['Boolean']['output']; + permission_patch: FPermissionOutput; stats_add: Scalars['Boolean']['output']; + tech_support_activate: Scalars['Boolean']['output']; + tech_support_set_config: Scalars['Boolean']['output']; thread_create: FThreadOutput; thread_delete: Scalars['Boolean']['output']; + thread_lock: Scalars['Boolean']['output']; thread_mass_group_patch: Scalars['Int']['output']; - thread_message_create: FThreadMessage; + thread_message_create: FThreadMessageOutput; thread_messages_create_multiple: FThreadMessagesCreateResult; thread_patch: FThreadOutput; + thread_provide_toolset: Scalars['Boolean']['output']; + thread_unlock: Scalars['Boolean']['output']; + workspace_invitation_create: FWorkspaceInvitationOutput; + workspace_invitation_delete: Scalars['Boolean']['output']; + workspace_invitation_patch: FWorkspaceInvitationOutput; }; @@ -374,6 +438,24 @@ export type MutationKnowledge_Item_PatchArgs = { }; +export type MutationPermission_CreateArgs = { + input: FPermissionInput; +}; + + +export type MutationPermission_DeleteArgs = { + fgroup_id: Scalars['String']['input']; + fuser_id: Scalars['String']['input']; +}; + + +export type MutationPermission_PatchArgs = { + fgroup_id: Scalars['String']['input']; + fuser_id: Scalars['String']['input']; + patch: FPermissionPatch; +}; + + export type MutationStats_AddArgs = { st_how_many: Scalars['Int']['input']; st_involved_expert?: Scalars['String']['input']; @@ -385,6 +467,17 @@ export type MutationStats_AddArgs = { }; +export type MutationTech_Support_ActivateArgs = { + ws_id: Scalars['String']['input']; +}; + + +export type MutationTech_Support_Set_ConfigArgs = { + config: TechSupportSettingsInput; + ws_id: Scalars['String']['input']; +}; + + export type MutationThread_CreateArgs = { input: FThreadInput; }; @@ -395,6 +488,12 @@ export type MutationThread_DeleteArgs = { }; +export type MutationThread_LockArgs = { + ft_id: Scalars['String']['input']; + worker_name: Scalars['String']['input']; +}; + + export type MutationThread_Mass_Group_PatchArgs = { dst_group_id: Scalars['String']['input']; src_group_id: Scalars['String']['input']; @@ -416,14 +515,62 @@ export type MutationThread_PatchArgs = { patch: FThreadPatch; }; + +export type MutationThread_Provide_ToolsetArgs = { + ft_id: Scalars['String']['input']; + toolset: Scalars['String']['input']; +}; + + +export type MutationThread_UnlockArgs = { + ft_id: Scalars['String']['input']; + worker_name: Scalars['String']['input']; +}; + + +export type MutationWorkspace_Invitation_CreateArgs = { + input: FWorkspaceInvitationInput; +}; + + +export type MutationWorkspace_Invitation_DeleteArgs = { + ws_id: Scalars['String']['input']; + wsi_email: Scalars['String']['input']; +}; + + +export type MutationWorkspace_Invitation_PatchArgs = { + patch: FWorkspaceInvitationPatch; + ws_id: Scalars['String']['input']; + wsi_email: Scalars['String']['input']; +}; + export type Query = { __typename?: 'Query'; + expert_get: FExpertOutput; expert_list: Array; + experts_effective_list: Array; + external_data_source_get: FExternalDataSourceOutput; external_data_source_list: Array; + knowledge_item_get: FKnowledgeItemOutput; knowledge_item_list: Array; - login: Array; + permission_get: FPermissionOutput; + permission_list: Array; + plugins_installed: Array; + query_basic_stuff: BasicStuffResult; + tech_support_get_config?: Maybe; + thread_get: FThreadOutput; thread_list: Array; - thread_messages_list: Array; + thread_messages_list: Array; + threads_app_captured: Array; + workspace_invitation_get: FWorkspaceInvitationOutput; + workspace_invitation_list: Array; + workspace_permission_list: Array; +}; + + +export type QueryExpert_GetArgs = { + id: Scalars['String']['input']; }; @@ -435,6 +582,16 @@ export type QueryExpert_ListArgs = { }; +export type QueryExperts_Effective_ListArgs = { + located_fgroup_id: Scalars['String']['input']; +}; + + +export type QueryExternal_Data_Source_GetArgs = { + id: Scalars['String']['input']; +}; + + export type QueryExternal_Data_Source_ListArgs = { limit: Scalars['Int']['input']; located_fgroup_id: Scalars['String']['input']; @@ -443,6 +600,11 @@ export type QueryExternal_Data_Source_ListArgs = { }; +export type QueryKnowledge_Item_GetArgs = { + id: Scalars['String']['input']; +}; + + export type QueryKnowledge_Item_ListArgs = { limit: Scalars['Int']['input']; located_fgroup_id: Scalars['String']['input']; @@ -451,6 +613,27 @@ export type QueryKnowledge_Item_ListArgs = { }; +export type QueryPermission_GetArgs = { + fgroup_id: Scalars['String']['input']; + fuser_id: Scalars['String']['input']; +}; + + +export type QueryPermission_ListArgs = { + fgroup_id: Scalars['String']['input']; +}; + + +export type QueryTech_Support_Get_ConfigArgs = { + ws_id: Scalars['String']['input']; +}; + + +export type QueryThread_GetArgs = { + id: Scalars['String']['input']; +}; + + export type QueryThread_ListArgs = { limit: Scalars['Int']['input']; located_fgroup_id: Scalars['String']['input']; @@ -464,6 +647,29 @@ export type QueryThread_Messages_ListArgs = { ftm_alt?: InputMaybe; }; + +export type QueryThreads_App_CapturedArgs = { + ft_app_capture: Scalars['String']['input']; + ft_app_searchable: Scalars['String']['input']; + located_fgroup_id: Scalars['String']['input']; +}; + + +export type QueryWorkspace_Invitation_GetArgs = { + ws_id: Scalars['String']['input']; + wsi_email: Scalars['String']['input']; +}; + + +export type QueryWorkspace_Invitation_ListArgs = { + ws_id: Scalars['String']['input']; +}; + + +export type QueryWorkspace_Permission_ListArgs = { + ws_id: Scalars['String']['input']; +}; + export type Subscription = { __typename?: 'Subscription'; comprehensive_thread_subs: FThreadMessageSubs; @@ -513,6 +719,23 @@ export type SubscriptionTree_SubscriptionArgs = { ws_id: Scalars['String']['input']; }; +export type TechSupportSettingsInput = { + support_api_key: Scalars['String']['input']; + support_channel_list: Array; + support_discord_key: Scalars['String']['input']; + support_fgroup_id: Scalars['String']['input']; + support_fuser_id: Scalars['String']['input']; +}; + +export type TechSupportSettingsOutput = { + __typename?: 'TechSupportSettingsOutput'; + support_api_key: Scalars['String']['output']; + support_channel_list: Array; + support_discord_key: Scalars['String']['output']; + support_fgroup_id: Scalars['String']['output']; + support_fuser_id: Scalars['String']['output']; +}; + export type TreeUpdateSubs = { __typename?: 'TreeUpdateSubs'; treeupd_action: Scalars['String']['output']; @@ -540,9 +763,9 @@ export type NavTreeSubsSubscription = { __typename?: 'Subscription', tree_subscr export type NavTreeWantWorkspacesQueryVariables = Exact<{ [key: string]: never; }>; -export type NavTreeWantWorkspacesQuery = { __typename?: 'Query', login: Array<{ __typename?: 'FWorkspace', ws_id: string, ws_owner_fuser_id: string, ws_root_group_id: string, root_group_name: string }> }; +export type NavTreeWantWorkspacesQuery = { __typename?: 'Query', query_basic_stuff: { __typename?: 'BasicStuffResult', fuser_id: string, workspaces: Array<{ __typename?: 'FWorkspace', ws_id: string, ws_owner_fuser_id: string, ws_root_group_id: string, root_group_name: string }> } }; export const CreateGroupDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateGroup"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"fgroup_name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"fgroup_parent_id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"group_create"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"fgroup_name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"fgroup_name"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"fgroup_parent_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"fgroup_parent_id"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fgroup_id"}},{"kind":"Field","name":{"kind":"Name","value":"fgroup_name"}},{"kind":"Field","name":{"kind":"Name","value":"ws_id"}},{"kind":"Field","name":{"kind":"Name","value":"fgroup_parent_id"}},{"kind":"Field","name":{"kind":"Name","value":"fgroup_created_ts"}}]}}]}}]} as unknown as DocumentNode; export const NavTreeSubsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"NavTreeSubs"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"ws_id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"tree_subscription"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"ws_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"ws_id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"treeupd_action"}},{"kind":"Field","name":{"kind":"Name","value":"treeupd_id"}},{"kind":"Field","name":{"kind":"Name","value":"treeupd_path"}},{"kind":"Field","name":{"kind":"Name","value":"treeupd_type"}},{"kind":"Field","name":{"kind":"Name","value":"treeupd_title"}}]}}]}}]} as unknown as DocumentNode; -export const NavTreeWantWorkspacesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"NavTreeWantWorkspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ws_id"}},{"kind":"Field","name":{"kind":"Name","value":"ws_owner_fuser_id"}},{"kind":"Field","name":{"kind":"Name","value":"ws_root_group_id"}},{"kind":"Field","name":{"kind":"Name","value":"root_group_name"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file +export const NavTreeWantWorkspacesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"NavTreeWantWorkspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"query_basic_stuff"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fuser_id"}},{"kind":"Field","name":{"kind":"Name","value":"workspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ws_id"}},{"kind":"Field","name":{"kind":"Name","value":"ws_owner_fuser_id"}},{"kind":"Field","name":{"kind":"Name","value":"ws_root_group_id"}},{"kind":"Field","name":{"kind":"Name","value":"root_group_name"}}]}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/refact-agent/gui/generated/schema.graphql b/refact-agent/gui/generated/schema.graphql index 125cbcd2b..a7a1e36c4 100644 --- a/refact-agent/gui/generated/schema.graphql +++ b/refact-agent/gui/generated/schema.graphql @@ -4,16 +4,19 @@ schema { subscription: Subscription } +type BasicStuffResult { + fuser_id: String! + workspaces: [FWorkspace!]! +} + input FExpertInput { fexp_allow_tools: String! fexp_block_tools: String! fexp_name: String! fexp_python_kernel: String! fexp_system_prompt: String! - fexp_ver_major: Int! - fexp_ver_minor: Int! located_fgroup_id: String! - owner_fuser_id: String! + owner_fuser_id: String owner_shared: Boolean! } @@ -23,10 +26,8 @@ type FExpertOutput { fexp_name: String! fexp_python_kernel: String! fexp_system_prompt: String! - fexp_ver_major: Int! - fexp_ver_minor: Int! - located_fgroup_id: String! - owner_fuser_id: String! + located_fgroup_id: String + owner_fuser_id: String owner_shared: Boolean! } @@ -39,11 +40,11 @@ type FExpertSubs { news_action: String! news_payload: FExpertOutput! news_payload_id: String! - news_ws_id: String! + news_pubsub: String! } input FExternalDataSourceInput { - eds_json: JSON! + eds_json: String! eds_name: String! eds_type: String! located_fgroup_id: String! @@ -64,7 +65,7 @@ type FExternalDataSourceOutput { } input FExternalDataSourcePatch { - eds_json: JSON = null + eds_json: String! eds_last_successful_scan_ts: Float = null eds_name: String = null eds_scan_status: String = null @@ -80,6 +81,7 @@ type FExternalDataSourceSubs { } input FKnowledgeItemInput { + iknow_is_core: Boolean! = false iknow_memory: String! iknow_tags: [String!]! = [] located_fgroup_id: String! @@ -89,6 +91,7 @@ input FKnowledgeItemInput { type FKnowledgeItemOutput { iknow_created_ts: Float! iknow_id: String! + iknow_is_core: Boolean! iknow_memory: String! iknow_modified_ts: Float! iknow_stat_correct: Int! @@ -101,6 +104,7 @@ type FKnowledgeItemOutput { } input FKnowledgeItemPatch { + iknow_is_core: Boolean = null iknow_memory: String = null iknow_tags: [String!] = null located_fgroup_id: String = null @@ -111,7 +115,29 @@ type FKnowledgeItemSubs { news_action: String! news_payload: FKnowledgeItemOutput news_payload_id: String! - news_ws_id: String! + news_pubsub: String! +} + +input FPermissionInput { + fgroup_id: String! + fuser_id: String! + perm_role: String! +} + +type FPermissionOutput { + fgroup_id: String! + fuser_id: String! + perm_role: String! +} + +input FPermissionPatch { + perm_role: String = null +} + +type FPluginOutput { + plugin_name: String! + plugin_setup_page: String! + plugin_version: String! } type FThreadDelta { @@ -120,56 +146,63 @@ type FThreadDelta { } input FThreadInput { - ft_belongs_to_fce_id: String + ft_app_capture: String! = "" + ft_app_searchable: String! = "" + ft_app_specific: String! = "null" + ft_error: String! = "null" ft_fexp_name: String! - ft_fexp_ver_major: Int! - ft_fexp_ver_minor: Int! - ft_max_new_tokens: Int! = 8192 - ft_model: String! = "" - ft_n: Int! = 1 - ft_temperature: Float! = 0 ft_title: String! + ft_toolset: String! = "null" located_fgroup_id: String! owner_shared: Boolean! + parent_ft_id: String = null } -type FThreadMessage { +input FThreadMessageInput { ftm_alt: Int! + ftm_app_specific: String = "null" ftm_belongs_to_ft_id: String! ftm_call_id: String! - ftm_content: JSON! - ftm_created_ts: Float! + ftm_content: String = "null" ftm_num: Int! ftm_prev_alt: Int! + ftm_provenance: String! ftm_role: String! - ftm_tool_calls: JSON - ftm_usage: JSON + ftm_tool_calls: String = "null" + ftm_usage: String = "null" + ftm_user_preferences: String = "null" } -input FThreadMessageInput { +type FThreadMessageOutput { + ft_app_capture: String + ft_app_searchable: String + ft_app_specific: JSON ftm_alt: Int! + ftm_app_specific: JSON ftm_belongs_to_ft_id: String! ftm_call_id: String! - ftm_content: String! + ftm_content: JSON + ftm_created_ts: Float! ftm_num: Int! ftm_prev_alt: Int! - ftm_provenance: String! + ftm_provenance: JSON! ftm_role: String! - ftm_tool_calls: String! - ftm_usage: String! + ftm_tool_calls: JSON + ftm_usage: JSON + ftm_user_preferences: JSON } type FThreadMessageSubs { news_action: String! news_payload_id: String! news_payload_thread: FThreadOutput - news_payload_thread_message: FThreadMessage + news_payload_thread_message: FThreadMessageOutput stream_delta: FThreadDelta } type FThreadMessagesCreateResult { count: Int! - messages: [FThreadMessage!]! + messages: [FThreadMessageOutput!]! } input FThreadMultipleMessagesInput { @@ -178,50 +211,46 @@ input FThreadMultipleMessagesInput { } type FThreadOutput { - ft_anything_new: Boolean! + ft_app_capture: String! + ft_app_searchable: String! + ft_app_specific: JSON ft_archived_ts: Float! - ft_belongs_to_fce_id: String ft_created_ts: Float! - ft_error: String! + ft_error: JSON ft_fexp_name: String! - ft_fexp_ver_major: Int! - ft_fexp_ver_minor: Int! ft_id: String! ft_locked_by: String! - ft_max_new_tokens: Int! - ft_model: String! - ft_n: Int! ft_need_assistant: Int! + ft_need_kernel: Int! ft_need_tool_calls: Int! - ft_temperature: Float! + ft_need_user: Int! ft_title: String! - ft_toolset: String! + ft_toolset: JSON ft_updated_ts: Float! located_fgroup_id: String! owner_fuser_id: String! owner_shared: Boolean! + parent_ft_id: String } input FThreadPatch { - ft_anything_new: Boolean = null + ft_app_searchable: String = null + ft_app_specific: String = null ft_archived_ts: Float = null - ft_belongs_to_fce_id: String = null ft_error: String = null - ft_max_new_tokens: Int = null - ft_model: String = null - ft_n: Int = null - ft_temperature: Float = null + ft_need_user: Int = null ft_title: String = null ft_toolset: String = null located_fgroup_id: String = null owner_shared: Boolean = null + parent_ft_id: String = null } type FThreadSubs { news_action: String! news_payload: FThreadOutput news_payload_id: String! - news_ws_id: String! + news_pubsub: String! } type FWorkspace { @@ -233,6 +262,26 @@ type FWorkspace { ws_status: String! } +input FWorkspaceInvitationInput { + ws_id: String! + wsi_email: String! + wsi_invited_by_fuser_id: String! + wsi_role: String! +} + +type FWorkspaceInvitationOutput { + ws_id: String! + wsi_created_ts: Float! + wsi_email: String! + wsi_invited_by_fuser_id: String! + wsi_role: String! + wsi_token: String! +} + +input FWorkspaceInvitationPatch { + wsi_role: String = null +} + type FlexusGroup { fgroup_created_ts: Float! fgroup_id: String! @@ -268,22 +317,46 @@ type Mutation { knowledge_item_delete(id: String!): Boolean! knowledge_item_mass_group_patch(dst_group_id: String!, src_group_id: String!): Int! knowledge_item_patch(id: String!, patch: FKnowledgeItemPatch!): FKnowledgeItemOutput! + permission_create(input: FPermissionInput!): FPermissionOutput! + permission_delete(fgroup_id: String!, fuser_id: String!): Boolean! + permission_patch(fgroup_id: String!, fuser_id: String!, patch: FPermissionPatch!): FPermissionOutput! stats_add(st_how_many: Int!, st_involved_expert: String! = "", st_involved_fuser_id: String! = "", st_involved_model: String! = "", st_thing: String!, ts: Float!, ws_id: String!): Boolean! + tech_support_activate(ws_id: String!): Boolean! + tech_support_set_config(config: TechSupportSettingsInput!, ws_id: String!): Boolean! thread_create(input: FThreadInput!): FThreadOutput! thread_delete(id: String!): Boolean! + thread_lock(ft_id: String!, worker_name: String!): Boolean! thread_mass_group_patch(dst_group_id: String!, src_group_id: String!): Int! - thread_message_create(input: FThreadMessageInput!): FThreadMessage! + thread_message_create(input: FThreadMessageInput!): FThreadMessageOutput! thread_messages_create_multiple(input: FThreadMultipleMessagesInput!): FThreadMessagesCreateResult! thread_patch(id: String!, patch: FThreadPatch!): FThreadOutput! + thread_provide_toolset(ft_id: String!, toolset: String!): Boolean! + thread_unlock(ft_id: String!, worker_name: String!): Boolean! + workspace_invitation_create(input: FWorkspaceInvitationInput!): FWorkspaceInvitationOutput! + workspace_invitation_delete(ws_id: String!, wsi_email: String!): Boolean! + workspace_invitation_patch(patch: FWorkspaceInvitationPatch!, ws_id: String!, wsi_email: String!): FWorkspaceInvitationOutput! } type Query { + expert_get(id: String!): FExpertOutput! expert_list(limit: Int!, located_fgroup_id: String!, skip: Int!, sort_by: String! = ""): [FExpertOutput!]! + experts_effective_list(located_fgroup_id: String!): [FExpertOutput!]! + external_data_source_get(id: String!): FExternalDataSourceOutput! external_data_source_list(limit: Int!, located_fgroup_id: String!, skip: Int!, sort_by: String! = ""): [FExternalDataSourceOutput!]! + knowledge_item_get(id: String!): FKnowledgeItemOutput! knowledge_item_list(limit: Int!, located_fgroup_id: String!, skip: Int!, sort_by: String! = ""): [FKnowledgeItemOutput!]! - login: [FWorkspace!]! + permission_get(fgroup_id: String!, fuser_id: String!): FPermissionOutput! + permission_list(fgroup_id: String!): [FPermissionOutput!]! + plugins_installed: [FPluginOutput!]! + query_basic_stuff: BasicStuffResult! + tech_support_get_config(ws_id: String!): TechSupportSettingsOutput + thread_get(id: String!): FThreadOutput! thread_list(limit: Int!, located_fgroup_id: String!, skip: Int!, sort_by: String! = ""): [FThreadOutput!]! - thread_messages_list(ft_id: String!, ftm_alt: Int = null): [FThreadMessage!]! + thread_messages_list(ft_id: String!, ftm_alt: Int = null): [FThreadMessageOutput!]! + threads_app_captured(ft_app_capture: String!, ft_app_searchable: String!, located_fgroup_id: String!): [FThreadOutput!]! + workspace_invitation_get(ws_id: String!, wsi_email: String!): FWorkspaceInvitationOutput! + workspace_invitation_list(ws_id: String!): [FWorkspaceInvitationOutput!]! + workspace_permission_list(ws_id: String!): [FPermissionOutput!]! } type Subscription { @@ -295,6 +368,22 @@ type Subscription { tree_subscription(ws_id: String!): TreeUpdateSubs! } +input TechSupportSettingsInput { + support_api_key: String! + support_channel_list: [String!]! + support_discord_key: String! + support_fgroup_id: String! + support_fuser_id: String! +} + +type TechSupportSettingsOutput { + support_api_key: String! + support_channel_list: [String!]! + support_discord_key: String! + support_fgroup_id: String! + support_fuser_id: String! +} + type TreeUpdateSubs { treeupd_action: String! treeupd_id: String! diff --git a/refact-agent/gui/src/components/Sidebar/GroupTree/ConfirmGroupSelection/ConfirmGroupSelection.tsx b/refact-agent/gui/src/components/Sidebar/GroupTree/ConfirmGroupSelection/ConfirmGroupSelection.tsx index 898a0ae06..5554c617c 100644 --- a/refact-agent/gui/src/components/Sidebar/GroupTree/ConfirmGroupSelection/ConfirmGroupSelection.tsx +++ b/refact-agent/gui/src/components/Sidebar/GroupTree/ConfirmGroupSelection/ConfirmGroupSelection.tsx @@ -1,5 +1,5 @@ import { motion } from "framer-motion"; -import { Button, Card, Checkbox, Flex, Heading, Text } from "@radix-ui/themes"; +import { Button, Card, Checkbox, Flex, Heading } from "@radix-ui/themes"; import { FlexusTreeNode } from "../GroupTree"; import React, { useCallback, useMemo, useState } from "react"; @@ -103,16 +103,12 @@ export const ConfirmGroupSelection: React.FC = ({ - Would you like to connect your current workspace in IDE to the  + Connecting current IDE workspace to the  {currentSelectedTeamsGroupNode.treenodeTitle} -  group? +  group - - This will help you synchronize your local workspace with the - selected group in the cloud. - {!isMatchingGroupNameWithWorkspace && ( = ({ htmlFor="create-folder-checkbox" className={styles.checkboxLabel} > - Create a subfolder{" "} + Create and select subfolder{" "} {`${currentSelectedTeamsGroupNode.treenodeTitle}/${currentWorkspaceName}`}{" "} - in current selected group )} diff --git a/refact-agent/gui/src/components/Sidebar/GroupTree/GroupTree.tsx b/refact-agent/gui/src/components/Sidebar/GroupTree/GroupTree.tsx index 6f6f4446d..6d7a8a654 100644 --- a/refact-agent/gui/src/components/Sidebar/GroupTree/GroupTree.tsx +++ b/refact-agent/gui/src/components/Sidebar/GroupTree/GroupTree.tsx @@ -43,17 +43,20 @@ export const GroupTree: React.FC = () => { - Source selection + Account selection - Select a cloud-based source associated with your team account to - continue. + Refact is even better connected to the cloud, you can share knowledge + database within your team. + + + Choose your team's account, or your personal account: - + {availableWorkspaces.map((workspace) => ( @@ -64,10 +67,9 @@ export const GroupTree: React.FC = () => { {availableWorkspaces.length === 0 && ( - No sources are currently associated with your team account. Please - contact your Team Workspace administrator to request access. For - further assistance, please refer to the support or bug reporting - channels. + No accounts are currently associated with your team. Please contact + your Team Workspace administrator to request access. For further + assistance, please refer to the support or bug reporting channels. )} @@ -80,14 +82,12 @@ export const GroupTree: React.FC = () => { justify="between" style={{ flex: 1, minHeight: 0 }} > - + Group selection - Choose a group within the selected source where your local - knowledge, chats, and features will be uploaded and synchronized - in the cloud. + If you have a lot of projects, you can organize them into groups: ([]); @@ -183,20 +184,21 @@ export function useGroupTree() { const onWorkspaceSelection = useCallback( (workspaceId: string) => { setCurrentTeamsWorkspace( - teamsWorkspaces.data?.login.find((w) => w.ws_id === workspaceId) ?? - null, + teamsWorkspaces.data?.query_basic_stuff.workspaces.find( + (w) => w.ws_id === workspaceId, + ) ?? null, ); setCurrentSelectedTeamsGroupNode(null); }, - [teamsWorkspaces.data?.login], + [teamsWorkspaces.data?.query_basic_stuff.workspaces], ); const availableWorkspaces = useMemo(() => { - if (teamsWorkspaces.data?.login) { - return teamsWorkspaces.data.login; + if (teamsWorkspaces.data?.query_basic_stuff.workspaces) { + return teamsWorkspaces.data.query_basic_stuff.workspaces; } return []; - }, [teamsWorkspaces.data?.login]); + }, [teamsWorkspaces.data?.query_basic_stuff.workspaces]); return { // Refs From 22fe1cd37c6830f5fb643faeee12a8053aa6f360 Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Wed, 4 Jun 2025 19:26:20 +0200 Subject: [PATCH 22/50] wip: refactored useIntegrations, got rid of formData handling --- .../Confirmation/Confirmation.tsx | 28 +- .../CustomFieldsAndWidgets.tsx | 103 ++-- .../Header/IntegrationsHeader.tsx | 8 +- .../FormAvailabilityAndDelete.tsx | 28 +- .../IntegrationForm/FormFields.tsx | 30 +- .../IntegrationForm/FormSmartlinks.tsx | 11 +- .../IntegrationAvailability.tsx | 2 +- .../IntegrationForm/IntegrationForm.tsx | 128 +---- .../IntegrationForm/MCPLogs.tsx | 1 - .../IntegrationsTable/ArgumentsTable.tsx | 176 ------ .../IntegrationsTable/ConfirmationTable.tsx | 19 +- .../IntegrationsTable/KeyValueTable.tsx | 27 +- .../IntegrationsTable/ParametersTable.tsx | 93 ++-- .../IntegrationsView/IntegrationsView.tsx | 28 +- .../IntermediateIntegration.tsx | 14 +- .../hooks/useFormAvailability.ts | 84 --- .../IntegrationsView/hooks/useIntegrations.ts | 515 ++++-------------- .../utils/prepareNotConfiguredIntegration.ts | 36 ++ .../Integrations/IntegrationFormField.tsx | 197 +++---- .../convertRawIntegrationFormValues.ts | 46 -- .../gui/src/services/refact/integrations.ts | 19 +- .../gui/src/utils/validateSnakeCase.ts | 12 +- 22 files changed, 446 insertions(+), 1159 deletions(-) delete mode 100644 refact-agent/gui/src/components/IntegrationsView/IntegrationsTable/ArgumentsTable.tsx delete mode 100644 refact-agent/gui/src/components/IntegrationsView/hooks/useFormAvailability.ts create mode 100644 refact-agent/gui/src/components/IntegrationsView/utils/prepareNotConfiguredIntegration.ts delete mode 100644 refact-agent/gui/src/features/Integrations/convertRawIntegrationFormValues.ts diff --git a/refact-agent/gui/src/components/IntegrationsView/Confirmation/Confirmation.tsx b/refact-agent/gui/src/components/IntegrationsView/Confirmation/Confirmation.tsx index aa3f11b6f..5f5867940 100644 --- a/refact-agent/gui/src/components/IntegrationsView/Confirmation/Confirmation.tsx +++ b/refact-agent/gui/src/components/IntegrationsView/Confirmation/Confirmation.tsx @@ -1,6 +1,7 @@ import type { FC } from "react"; import { useMemo } from "react"; import { + IntegrationFieldValue, SchemaToolConfirmation, ToolConfirmation, } from "../../../services/refact"; @@ -9,10 +10,10 @@ import { ConfirmationTable } from "../IntegrationsTable/ConfirmationTable"; import isEqual from "lodash.isequal"; type ConfirmationProps = { - confirmationByUser: ToolConfirmation; + confirmationByUser: ToolConfirmation | null; confirmationFromValues: ToolConfirmation | null; defaultConfirmationObject: SchemaToolConfirmation; - onChange: (fieldName: string, values: string[]) => void; + onChange: (fieldKey: string, fieldValue: IntegrationFieldValue) => void; }; export const Confirmation: FC = ({ @@ -47,14 +48,21 @@ export const Confirmation: FC = ({ confirmation request or the command will be blocked completely. - {Object.entries(confirmationObjectToRender).map(([key, values]) => ( - - ))} + {confirmationObjectToRender && + Object.entries(confirmationObjectToRender).map(([key, values]) => ( + { + // Update the nested confirmation field + onChange("confirmation", { + ...confirmationObjectToRender, + [tableName]: data, + }); + }} + /> + ))} ); diff --git a/refact-agent/gui/src/components/IntegrationsView/CustomFieldsAndWidgets.tsx b/refact-agent/gui/src/components/IntegrationsView/CustomFieldsAndWidgets.tsx index a8fdb2008..cced19dbf 100644 --- a/refact-agent/gui/src/components/IntegrationsView/CustomFieldsAndWidgets.tsx +++ b/refact-agent/gui/src/components/IntegrationsView/CustomFieldsAndWidgets.tsx @@ -1,19 +1,10 @@ -import { - Box, - Checkbox, - TextField, - TextArea, - Button, - Text, - Switch, -} from "@radix-ui/themes"; +import { Box, TextField, TextArea, Text, Switch } from "@radix-ui/themes"; import { Markdown } from "../Markdown"; -import { type ChangeEventHandler, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; // Custom Input Field export const CustomInputField = ({ value, - defaultValue, placeholder, type, id, @@ -21,8 +12,10 @@ export const CustomInputField = ({ size = "long", color = "gray", onChange, + wasInteracted = false, }: { id?: string; + wasInteracted?: boolean; type?: | "number" | "search" @@ -39,13 +32,21 @@ export const CustomInputField = ({ | "week"; value?: string; name?: string; - defaultValue?: string | number; placeholder?: string; size?: string; width?: string; color?: TextField.RootProps["color"]; - onChange?: ChangeEventHandler; + onChange?: (value: string) => void; }) => { + const wasInitialized = useRef(wasInteracted); + // a little hacky, but in this way we avoid of use of formData + useEffect(() => { + if (!wasInitialized.current && onChange) { + onChange(value ?? ""); + wasInitialized.current = true; + } + }, [onChange, value]); + return ( {size !== "multiline" ? ( @@ -57,9 +58,8 @@ export const CustomInputField = ({ value={value} variant="soft" color={color} - defaultValue={defaultValue} placeholder={placeholder} - onChange={onChange} + onChange={(e) => onChange?.(e.target.value)} /> ) : (