diff --git a/aider/__init__.py b/aider/__init__.py index 5271e256d1a..8ae2fd4d7c3 100644 --- a/aider/__init__.py +++ b/aider/__init__.py @@ -1,6 +1,6 @@ from packaging import version -__version__ = "0.88.29.dev" +__version__ = "0.88.30.dev" safe_version = __version__ try: diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 8409f3e3bee..25dd552131e 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -2989,10 +2989,12 @@ async def show_send_output_stream(self, completion): if tool_call_chunk.id: self.partial_response_tool_calls[index]["id"] = tool_call_chunk.id + if tool_call_chunk.type: self.partial_response_tool_calls[index][ "type" ] = tool_call_chunk.type + self.io.update_spinner_suffix(tool_call_chunk.type) if ( hasattr(tool_call_chunk, "provider_specific_fields") @@ -3022,6 +3024,7 @@ async def show_send_output_stream(self, completion): if tool_call_chunk.function: if "function" not in self.partial_response_tool_calls[index]: self.partial_response_tool_calls[index]["function"] = {} + if tool_call_chunk.function.name: if ( "name" @@ -3033,6 +3036,8 @@ async def show_send_output_stream(self, completion): self.partial_response_tool_calls[index]["function"][ "name" ] += tool_call_chunk.function.name + self.io.update_spinner_suffix(tool_call_chunk.function.name) + if tool_call_chunk.function.arguments: if ( "arguments" @@ -3044,6 +3049,10 @@ async def show_send_output_stream(self, completion): self.partial_response_tool_calls[index]["function"][ "arguments" ] += tool_call_chunk.function.arguments + self.io.update_spinner_suffix( + tool_call_chunk.function.arguments + ) + except (AttributeError, IndexError): # Handle cases where the response structure doesn't match expectations pass @@ -3059,6 +3068,8 @@ async def show_send_output_stream(self, completion): else: self.partial_response_function_call[k] = v + self.io.update_spinner_suffix(v) + received_content = True except AttributeError: pass @@ -3079,6 +3090,7 @@ async def show_send_output_stream(self, completion): text += reasoning_content self.got_reasoning_content = True received_content = True + self.io.update_spinner_suffix(reasoning_content) try: content = chunk.choices[0].delta.content @@ -3089,6 +3101,7 @@ async def show_send_output_stream(self, completion): text += content received_content = True + self.io.update_spinner_suffix(content) except AttributeError: pass @@ -3671,7 +3684,7 @@ async def auto_save_session(self): if not hasattr(self, "_autosave_future"): self._autosave_future = None - if self._autosave_future and self._autosave_future.done(): + if self._autosave_future and not self._autosave_future.done(): return # Throttle autosave to run at most once every 15 seconds diff --git a/aider/coders/chat_chunks.py b/aider/coders/chat_chunks.py index 060099a61f1..f5bdf5f8918 100644 --- a/aider/coders/chat_chunks.py +++ b/aider/coders/chat_chunks.py @@ -18,9 +18,9 @@ def all_messages(self): self.system + self.examples + self.readonly_files + + self.chat_files + self.repo + self.done - + self.chat_files + self.cur + self.reminder ) @@ -31,15 +31,17 @@ def add_cache_control_headers(self): else: self.add_cache_control(self.system) - if self.repo: - # this will mark both the readonly_files and repomap chunk as cacheable - self.add_cache_control(self.repo) - else: - # otherwise, just cache readonly_files if there are any - self.add_cache_control(self.readonly_files) - + # The files form a cacheable block. + # The block starts with readonly_files and ends with chat_files. + # So we mark the end of chat_files. self.add_cache_control(self.chat_files) + # The repo map is its own cacheable block. + self.add_cache_control(self.repo) + + # The history is ephemeral on its own. + self.add_cache_control(self.done) + def add_cache_control(self, messages): if not messages: return diff --git a/aider/io.py b/aider/io.py index 0323d9b3b11..4a754503e15 100644 --- a/aider/io.py +++ b/aider/io.py @@ -446,6 +446,7 @@ def __init__( self.spinner_running = False self.spinner_text = "" self.last_spinner_text = "" + self.spinner_suffix = "" self.spinner_frame_index = 0 self.spinner_last_frame_index = 0 self.unicode_palette = "░█" @@ -513,6 +514,7 @@ def start_spinner(self, text, update_last_text=True): self.spinner_running = True self.spinner_text = text self.spinner_frame_index = self.spinner_last_frame_index + self.spinner_suffix = "" if update_last_text: self.last_spinner_text = text @@ -523,10 +525,18 @@ def start_spinner(self, text, update_last_text=True): def update_spinner(self, text): self.spinner_text = text + def update_spinner_suffix(self, text=None): + if text: + self.spinner_suffix = f" • {text[:16].strip()}" + else: + self.spinner_suffix = "" + def stop_spinner(self): """Stop the spinner.""" self.spinner_running = False self.spinner_text = "" + self.spinner_suffix = "" + # Keep last frame index to avoid spinner "jumping" on restart self.spinner_last_frame_index = self.spinner_frame_index if self.fallback_spinner: @@ -541,7 +551,7 @@ def get_bottom_toolbar(self): frame = self.spinner_frames[self.spinner_frame_index] self.spinner_frame_index = (self.spinner_frame_index + 1) % len(self.spinner_frames) - return f"{frame} {self.spinner_text}" + return f"{frame} {self.spinner_text}{self.spinner_suffix}" def _validate_color_settings(self): """Validate configured color strings and reset invalid ones.""" diff --git a/aider/models.py b/aider/models.py index 652061551b9..81a34efaec6 100644 --- a/aider/models.py +++ b/aider/models.py @@ -982,7 +982,7 @@ async def send_completion( { "location": "message", "role": "system", - } + }, ] # Are we using github copilot?