From 60e838df9faf01c913045661fe39630065c13165 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 19:49:37 -0300 Subject: [PATCH] roughed in faster completions --- aider/commands.py | 52 +++++++++++++++++++++-------------------------- aider/io.py | 40 ++++++++++++++++++++++++------------ 2 files changed, 50 insertions(+), 42 deletions(-) diff --git a/aider/commands.py b/aider/commands.py index b3101c82c..3012e2b56 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -5,7 +5,6 @@ import sys from pathlib import Path import git -from prompt_toolkit.completion import Completion from aider import models, prompts, voice from aider.litellm import litellm @@ -41,11 +40,9 @@ class Commands: models.sanity_check_models(self.io, model) raise SwitchModel(model) - def completions_model(self, partial): - models = litellm.model_cost.keys() - for model in models: - if partial.lower() in model.lower(): - yield Completion(model, start_position=-len(partial)) + def completions_model(self): + models = [] # litellm.model_cost.keys() + return models def cmd_models(self, args): "Search the list of available models" @@ -82,21 +79,23 @@ class Commands: def is_command(self, inp): return inp[0] in "/!" - def get_commands(self): - commands = [] + def get_commands(self, with_completions=False): + commands = dict() for attr in dir(self): - if attr.startswith("cmd_"): - commands.append("/" + attr[4:]) + if not attr.startswith("cmd_"): + continue + + cmd = attr[4:] + completions = getattr(self, f"completions_{cmd}", None) + if completions and with_completions: + completions = sorted(completions()) + else: + completions = [] + + commands["/" + cmd] = completions return commands - def get_command_completions(self, cmd_name, partial): - cmd_completions_method_name = f"completions_{cmd_name}" - cmd_completions_method = getattr(self, cmd_completions_method_name, None) - if cmd_completions_method: - for completion in cmd_completions_method(partial): - yield completion - def do_run(self, cmd_name, args): cmd_method_name = f"cmd_{cmd_name}" cmd_method = getattr(self, cmd_method_name, None) @@ -113,7 +112,7 @@ class Commands: first_word = words[0] rest_inp = inp[len(words[0]) :] - all_commands = self.get_commands() + all_commands = self.get_commands().keys() matching_commands = [cmd for cmd in all_commands if cmd.startswith(first_word)] return matching_commands, first_word, rest_inp @@ -377,12 +376,10 @@ class Commands: fname = f'"{fname}"' return fname - def completions_add(self, partial): + def completions_add(self): files = set(self.coder.get_all_relative_files()) files = files - set(self.coder.get_inchat_relative_files()) - for fname in files: - if partial.lower() in fname.lower(): - yield Completion(self.quote_fname(fname), start_position=-len(partial)) + return files def glob_filtered_to_repo(self, pattern): try: @@ -483,12 +480,9 @@ class Commands: reply = prompts.added_files.format(fnames=", ".join(added_fnames)) return reply - def completions_drop(self, partial): + def completions_drop(self): files = self.coder.get_inchat_relative_files() - - for fname in files: - if partial.lower() in fname.lower(): - yield Completion(self.quote_fname(fname), start_position=-len(partial)) + return files def cmd_drop(self, args=""): "Remove files from the chat session to free up context space" @@ -624,7 +618,7 @@ class Commands: def cmd_help(self, args): "Show help about all commands" - commands = sorted(self.get_commands()) + commands = sorted(self.get_commands().keys()) for cmd in commands: cmd_method_name = f"cmd_{cmd[1:]}" cmd_method = getattr(self, cmd_method_name, None) @@ -638,7 +632,7 @@ class Commands: "Show help about all commands in markdown" res = "" - commands = sorted(self.get_commands()) + commands = sorted(self.get_commands().keys()) for cmd in commands: cmd_method_name = f"cmd_{cmd[1:]}" cmd_method = getattr(self, cmd_method_name, None) diff --git a/aider/io.py b/aider/io.py index 1cf09f41f..b08033df1 100644 --- a/aider/io.py +++ b/aider/io.py @@ -23,7 +23,6 @@ from .utils import is_image_file class AutoCompleter(Completer): def __init__(self, root, rel_fnames, addable_rel_fnames, commands, encoding): - self.commands = commands self.addable_rel_fnames = addable_rel_fnames self.rel_fnames = rel_fnames self.encoding = encoding @@ -37,6 +36,13 @@ class AutoCompleter(Completer): self.words = set() + import time + + start = time.time() + self.commands = commands.get_commands(with_completions=True) + dur = time.time() - start + dump(dur) + for rel_fname in addable_rel_fnames: self.words.add(rel_fname) @@ -64,16 +70,24 @@ class AutoCompleter(Completer): if text[0] == "/": if len(words) == 1 and not text[-1].isspace(): - candidates = self.commands.get_commands() - candidates = [(cmd, cmd) for cmd in candidates] - else: - for completion in self.commands.get_command_completions(words[0][1:], words[-1]): - yield completion - return - else: - candidates = self.words - candidates.update(set(self.fname_to_rel_fnames)) - candidates = [(word, f"`{word}`") for word in candidates] + partial = words[0] + candidates = self.commands.keys() + for cmd in candidates: + if cmd.startswith(partial): + yield Completion(cmd, start_position=-len(partial)) + elif len(words) > 1: + cmd = words[0] + partial = words[-1] + + candidates = self.commands.get(cmd, []) + for word in candidates: + if partial in word: + yield Completion(word, start_position=-len(partial)) + return + + candidates = self.words + candidates.update(set(self.fname_to_rel_fnames)) + candidates = [(word, f"`{word}`") for word in candidates] last_word = words[-1] for word_match, word_insert in candidates: @@ -277,8 +291,8 @@ class InputOutput: def log_llm_history(self, role, content): if not self.llm_history_file: return - timestamp = datetime.now().isoformat(timespec='seconds') - with open(self.llm_history_file, 'a', encoding=self.encoding) as log_file: + timestamp = datetime.now().isoformat(timespec="seconds") + with open(self.llm_history_file, "a", encoding=self.encoding) as log_file: log_file.write(f"{role.upper()} {timestamp}\n") log_file.write(content + "\n")