From cd84cc0d3ede4960dc871a9df39022d2e2be8b09 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sun, 5 May 2024 18:58:16 -0700 Subject: [PATCH 1/9] refactored summarize_chat_history_markdown --- aider/history.py | 60 ++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/aider/history.py b/aider/history.py index f8ea7cdd4..173e042ef 100644 --- a/aider/history.py +++ b/aider/history.py @@ -1,6 +1,6 @@ import argparse -from aider import prompts +from aider import models, prompts from aider.dump import dump # noqa: F401 from aider.sendchat import simple_send_with_retries @@ -90,41 +90,47 @@ class ChatSummary: return [dict(role="user", content=summary)] + def summarize_chat_history_markdown(self, text): + messages = [] + assistant = [] + for line in text.splitlines(keepends=True): + if line.startswith("# "): + continue + if line.startswith(">"): + continue + if line.startswith("#### /"): + continue + + if line.startswith("#### "): + if assistant: + assistant = "".join(assistant) + if assistant.strip(): + messages.append(dict(role="assistant", content=assistant)) + assistant = [] + + content = line[5:] + if content.strip() and content.strip() != "": + messages.append(dict(role="user", content=line[5:])) + continue + + assistant.append(line) + + summary = self.summarize(messages[-40:]) + return summary + def main(): parser = argparse.ArgumentParser() parser.add_argument("filename", help="Markdown file to parse") args = parser.parse_args() + model = models.Model("gpt-3.5-turbo") + summarizer = ChatSummary(model) + with open(args.filename, "r") as f: text = f.read() - messages = [] - assistant = [] - for line in text.splitlines(keepends=True): - if line.startswith("# "): - continue - if line.startswith(">"): - continue - if line.startswith("#### /"): - continue - - if line.startswith("#### "): - if assistant: - assistant = "".join(assistant) - if assistant.strip(): - messages.append(dict(role="assistant", content=assistant)) - assistant = [] - - content = line[5:] - if content.strip() and content.strip() != "": - messages.append(dict(role="user", content=line[5:])) - continue - - assistant.append(line) - - summarizer = ChatSummary("gpt-3.5-turbo", weak_model=False) - summary = summarizer.summarize(messages[-40:]) + summary = summarizer.summarize_chat_history_markdown(text) dump(summary) From b3e19cf5bee2cb150b9ef58419a97b44afd74357 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sun, 5 May 2024 19:03:17 -0700 Subject: [PATCH 2/9] reload history --- aider/coders/base_coder.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index b4ed725d2..561f1d3bf 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -289,6 +289,12 @@ class Coder: self.main_model.max_chat_history_tokens, ) + if not self.done_messages: + history_md = self.io.read_text(self.io.chat_history_file) + dump(history_md) + self.done_messages = self.summarizer.summarize_chat_history_markdown(history_md) + dump(self.done_messages) + self.summarizer_thread = None self.summarized_done_messages = [] From 00c12efd3f49b271458bec3827d21589bf2d537b Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sun, 5 May 2024 19:04:24 -0700 Subject: [PATCH 3/9] cleanup --- aider/coders/base_coder.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 561f1d3bf..6a2998626 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -291,9 +291,8 @@ class Coder: if not self.done_messages: history_md = self.io.read_text(self.io.chat_history_file) - dump(history_md) - self.done_messages = self.summarizer.summarize_chat_history_markdown(history_md) - dump(self.done_messages) + if history_md: + self.done_messages = self.summarizer.summarize_chat_history_markdown(history_md) self.summarizer_thread = None self.summarized_done_messages = [] From eac8e4cb8f2658ef696feff22cd41b12003d55be Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sun, 5 May 2024 19:08:00 -0700 Subject: [PATCH 4/9] announce reload of chat history --- aider/coders/base_coder.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 6a2998626..b5302122a 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -167,6 +167,9 @@ class Coder: for fname in self.get_inchat_relative_files(): lines.append(f"Added {fname} to the chat.") + if self.done_messages: + lines.append("Restored previous conversation history.") + return lines def __init__( From 3a79b6230b0ff5173bf2b0f92dd08ee3c8300dcd Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 6 May 2024 09:23:37 -0700 Subject: [PATCH 5/9] aider: Added a --max-chat-history-tokens switch and passed it to ChatSummary in Coder. # Aider chat conversation: USER: add a --max-chat-history-tokens switch, pass it into Coder to `ChatSummary` ASSISTANT: Here are the changes to add a --max-chat-history-tokens switch and pass it to ChatSummary in Coder: aider/args.py ```python <<<<<<< SEARCH parser.add_argument( "--map-tokens", type=int, default=1024, help="Number of tokens to use for the repo map. Set to 0 to disable.", ) ======= parser.add_argument( "--map-tokens", type=int, default=1024, help="Number of tokens to use for the repo map. Set to 0 to disable.", ) parser.add_argument( "--max-chat-history-tokens", type=int, default=None, help="Maximum number of tokens to use for chat history. If not specified, uses the model's max_chat_history_tokens.", ) >>>>>>> REPLACE ``` aider/coders/base_coder.py ```python <<<<<<< SEARCH self.summarizer = ChatSummary( self.main_model.weak_model, self.main_model.max_chat_history_tokens, ) ======= max_chat_history_tokens = self.main_model.max_chat_history_tokens if kwargs.get("max_chat_history_tokens") is not None: max_chat_history_tokens = kwargs["max_chat_history_tokens"] self.summarizer = ChatSummary( self.main_model.weak_model, max_chat_history_tokens, ) >>>>>>> REPLACE ``` aider/main.py ```python <<<<<<< SEARCH map_tokens=args.map_tokens, ======= map_tokens=args.map_tokens, max_chat_history_tokens=args.max_chat_history_tokens, >>>>>>> REPLACE ``` This adds the new command line argument `--max-chat-history-tokens` which defaults to None. If not specified, it will use the model's default `max_chat_history_tokens`. It passes this argument from `main()` into the `Coder` constructor. In the `Coder` constructor, it checks if `max_chat_history_tokens` was provided in the `kwargs`. If so, it uses that value, otherwise it falls back to the model's default. Let me know if you have any other questions! ASSISTANT: Here are the changes to add a --max-chat-history-tokens switch and pass it to ChatSummary in Coder: aider/args.py ```python <<<<<<< SEARCH parser.add_argument( "--map-tokens", type=int, default=1024, help="Number of tokens to use for the repo map. Set to 0 to disable.", ) ======= parser.add_argument( "--map-tokens", type=int, default=1024, help="Number of tokens to use for the repo map. Set to 0 to disable.", ) parser.add_argument( "--max-chat-history-tokens", type=int, default=None, help="Maximum number of tokens to use for chat history. If not specified, uses the model's max_chat_history_tokens.", ) >>>>>>> REPLACE ``` aider/coders/base_coder.py ```python <<<<<<< SEARCH self.summarizer = ChatSummary( self.main_model.weak_model, self.main_model.max_chat_history_tokens, ) ======= max_chat_history_tokens = self.main_model.max_chat_history_tokens if kwargs.get("max_chat_history_tokens") is not None: max_chat_history_tokens = kwargs["max_chat_history_tokens"] self.summarizer = ChatSummary( self.main_model.weak_model, max_chat_history_tokens, ) >>>>>>> REPLACE ``` aider/main.py ```python <<<<<<< SEARCH map_tokens=args.map_tokens, ======= map_tokens=args.map_tokens, max_chat_history_tokens=args.max_chat_history_tokens, >>>>>>> REPLACE ``` This adds the new command line argument `--max-chat-history-tokens` which defaults to None. If not specified, it will use the model's default `max_chat_history_tokens`. It passes this argument from `main()` into the `Coder` constructor. In the `Coder` constructor, it checks if `max_chat_history_tokens` was provided in the `kwargs`. If so, it uses that value, otherwise it falls back to the model's default. Let me know if you have any other questions! USER: InvalidEditBlock: edit failed! aider/args.py does not contain the *exact chunk* of SEARCH lines you specified. Try again. DO NOT skip blank lines, comments, docstrings, etc! The SEARCH block needs to be EXACTLY the same as the lines in aider/args.py with nothing missing! aider/args.py does not contain these 6 exact lines in a row: ``` parser.add_argument( "--map-tokens", type=int, default=1024, help="Number of tokens to use for the repo map. Set to 0 to disable.", ) ``` ASSISTANT: I apologize for the mistake. Here is the corrected change for aider/args.py: aider/args.py ```python <<<<<<< SEARCH group.add_argument( "--map-tokens", type=int, default=1024, help="Max number of tokens to use for repo map, use 0 to disable (default: 1024)", ) ======= group.add_argument( "--map-tokens", type=int, default=1024, help="Max number of tokens to use for repo map, use 0 to disable (default: 1024)", ) group.add_argument( "--max-chat-history-tokens", type=int, default=None, help="Maximum number of tokens to use for chat history. If not specified, uses the model's max_chat_history_tokens.", ) >>>>>>> REPLACE ``` --- aider/args.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/aider/args.py b/aider/args.py index b298fd8f8..6a7d0c7b6 100644 --- a/aider/args.py +++ b/aider/args.py @@ -151,6 +151,12 @@ def get_parser(default_config_files, git_root): default=1024, help="Max number of tokens to use for repo map, use 0 to disable (default: 1024)", ) + group.add_argument( + "--max-chat-history-tokens", + type=int, + default=None, + help="Maximum number of tokens to use for chat history. If not specified, uses the model's max_chat_history_tokens.", + ) default_env_file = os.path.join(git_root, ".env") if git_root else ".env" group.add_argument( "--env-file", From 1747525418ee080205407eefe5ef677c4445f2d8 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 6 May 2024 09:25:04 -0700 Subject: [PATCH 6/9] aider: Added `max_chat_history_tokens` as an explicit argument to the `Coder.__init__` method and passed `args.max_chat_history_tokens` when creating the `Coder` instance in `main()`. # Aider chat conversation: USER: don't use kwargs, add it as an explicit arg ASSISTANT: Here is the *SEARCH/REPLACE block* to add `max_chat_history_tokens` as an explicit argument instead of using `**kwargs`: aider/coders/base_coder.py ```python <<<<<<< SEARCH def __init__( self, main_model, io, fnames=None, git_dname=None, pretty=True, show_diffs=False, auto_commits=True, dirty_commits=True, dry_run=False, map_tokens=1024, verbose=False, assistant_output_color="blue", code_theme="default", stream=True, use_git=True, voice_language=None, aider_ignore_file=None, cur_messages=None, done_messages=None, ): ======= def __init__( self, main_model, io, fnames=None, git_dname=None, pretty=True, show_diffs=False, auto_commits=True, dirty_commits=True, dry_run=False, map_tokens=1024, verbose=False, assistant_output_color="blue", code_theme="default", stream=True, use_git=True, voice_language=None, aider_ignore_file=None, cur_messages=None, done_messages=None, max_chat_history_tokens=None, ): >>>>>>> REPLACE ``` aider/main.py ```python <<<<<<< SEARCH coder = Coder.create( main_model=main_model, edit_format=args.edit_format, io=io, ## fnames=fnames, git_dname=git_dname, pretty=args.pretty, show_diffs=args.show_diffs, auto_commits=args.auto_commits, dirty_commits=args.dirty_commits, dry_run=args.dry_run, map_tokens=args.map_tokens, verbose=args.verbose, assistant_output_color=args.assistant_output_color, code_theme=args.code_theme, stream=args.stream, use_git=args.git, voice_language=args.voice_language, aider_ignore_file=args.aiderignore, ) ======= coder = Coder.create( main_model=main_model, edit_format=args.edit_format, io=io, ## fnames=fnames, git_dname=git_dname, pretty=args.pretty, show_diffs=args.show_diffs, auto_commits=args.auto_commits, dirty_commits=args.dirty_commits, dry_run=args.dry_run, map_tokens=args.map_tokens, verbose=args.verbose, assistant_output_color=args.assistant_output_color, code_theme=args.code_theme, stream=args.stream, use_git=args.git, voice_language=args.voice_language, aider_ignore_file=args.aiderignore, max_chat_history_tokens=args.max_chat_history_tokens, ) >>>>>>> REPLACE ``` This adds `max_chat_history_tokens` as an explicit argument to the `Coder.__init__` method, and passes `args.max_chat_history_tokens` when creating the `Coder` instance in `main()`. --- aider/coders/base_coder.py | 1 + aider/main.py | 1 + 2 files changed, 2 insertions(+) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index b5302122a..49c035eda 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -193,6 +193,7 @@ class Coder: aider_ignore_file=None, cur_messages=None, done_messages=None, + max_chat_history_tokens=None, ): if not fnames: fnames = [] diff --git a/aider/main.py b/aider/main.py index 1bba9cdd0..ef45b7fc0 100644 --- a/aider/main.py +++ b/aider/main.py @@ -334,6 +334,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F use_git=args.git, voice_language=args.voice_language, aider_ignore_file=args.aiderignore, + max_chat_history_tokens=args.max_chat_history_tokens, ) except ValueError as err: From 64470955d4799b92a4b8174856e8699b5ba41568 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 6 May 2024 09:29:21 -0700 Subject: [PATCH 7/9] pass max_chat_history_tokens into ChatSummary --- aider/coders/base_coder.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 49c035eda..0b0dc8653 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -288,9 +288,11 @@ class Coder: self.verbose, ) + if max_chat_history_tokens is None: + max_chat_history_tokens = self.main_model.max_chat_history_tokens self.summarizer = ChatSummary( self.main_model.weak_model, - self.main_model.max_chat_history_tokens, + max_chat_history_tokens, ) if not self.done_messages: From e51e0219eeabc4be0c676d5ded21991c4d686e02 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 6 May 2024 09:31:32 -0700 Subject: [PATCH 8/9] aider: Add tqdm progress bar to `summarize_chat_history_markdown` loop. # Aider chat conversation: USER: add a tqdm to `summarize_chat_history_markdown` loop ASSISTANT: ```diff --- aider/history.py +++ aider/history.py @@ ... @@ from aider.sendchat import simple_send_with_retries +from tqdm import tqdm class ChatSummary: def __init__(self, model=None, max_tokens=1024): @@ ... @@ def summarize_chat_history_markdown(self, text): messages = [] assistant = [] + lines = text.splitlines(keepends=True) + for line in tqdm(lines, desc="Summarizing chat history"): - for line in text.splitlines(keepends=True): if line.startswith("# "): continue if line.startswith(">"): continue if line.startswith("#### /"): continue if line.startswith("#### "): if assistant: assistant = "".join(assistant) if assistant.strip(): messages.append(dict(role="assistant", content=assistant)) assistant = [] content = line[5:] if content.strip() and content.strip() != "": messages.append(dict(role="user", content=line[5:])) continue assistant.append(line) summary = self.summarize(messages[-40:]) return summary ``` This diff adds the `tqdm` progress bar to the `summarize_chat_history_markdown` method in the `aider/history.py` file. It wraps the loop that processes the chat history lines with `tqdm`, providing a description for the progress bar. --- aider/history.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aider/history.py b/aider/history.py index 173e042ef..7198fbfee 100644 --- a/aider/history.py +++ b/aider/history.py @@ -4,6 +4,7 @@ from aider import models, prompts from aider.dump import dump # noqa: F401 from aider.sendchat import simple_send_with_retries +from tqdm import tqdm class ChatSummary: def __init__(self, model=None, max_tokens=1024): @@ -93,7 +94,8 @@ class ChatSummary: def summarize_chat_history_markdown(self, text): messages = [] assistant = [] - for line in text.splitlines(keepends=True): + lines = text.splitlines(keepends=True) + for line in tqdm(lines, desc="Summarizing chat history"): if line.startswith("# "): continue if line.startswith(">"): From e61857ef09db263ee8de89ac2cb16e1bb3ffa36e Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 6 May 2024 09:47:14 -0700 Subject: [PATCH 9/9] summarize as many messages as will fit into the summarizer context --- aider/coders/base_coder.py | 9 +++++---- aider/history.py | 24 ++++++++++++++++++------ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 0b0dc8653..87bc689ca 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -295,13 +295,14 @@ class Coder: max_chat_history_tokens, ) + self.summarizer_thread = None + self.summarized_done_messages = [] + if not self.done_messages: history_md = self.io.read_text(self.io.chat_history_file) if history_md: - self.done_messages = self.summarizer.summarize_chat_history_markdown(history_md) - - self.summarizer_thread = None - self.summarized_done_messages = [] + self.done_messages = self.summarizer.split_chat_history_markdown(history_md) + self.summarize_start() # validate the functions jsonschema if self.functions: diff --git a/aider/history.py b/aider/history.py index 7198fbfee..a93e3141c 100644 --- a/aider/history.py +++ b/aider/history.py @@ -4,7 +4,6 @@ from aider import models, prompts from aider.dump import dump # noqa: F401 from aider.sendchat import simple_send_with_retries -from tqdm import tqdm class ChatSummary: def __init__(self, model=None, max_tokens=1024): @@ -57,7 +56,21 @@ class ChatSummary: head = messages[:split_index] tail = messages[split_index:] - summary = self.summarize_all(head) + sized = sized[:split_index] + head.reverse() + sized.reverse() + keep = [] + total = 0 + model_max_input_tokens = self.model.info.get("max_input_tokens", 4096) - 512 + for i in range(split_index): + total += sized[i][0] + if total > model_max_input_tokens: + break + keep.append(head[i]) + + keep.reverse() + + summary = self.summarize_all(keep) tail_tokens = sum(tokens for tokens, msg in sized[split_index:]) summary_tokens = self.token_count(summary) @@ -91,11 +104,11 @@ class ChatSummary: return [dict(role="user", content=summary)] - def summarize_chat_history_markdown(self, text): + def split_chat_history_markdown(self, text): messages = [] assistant = [] lines = text.splitlines(keepends=True) - for line in tqdm(lines, desc="Summarizing chat history"): + for line in lines: if line.startswith("# "): continue if line.startswith(">"): @@ -117,8 +130,7 @@ class ChatSummary: assistant.append(line) - summary = self.summarize(messages[-40:]) - return summary + return messages def main():