From 76414a447e84aef41350609757aa6a7bb5fcc018 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 13 Apr 2026 22:02:45 -0700 Subject: [PATCH] cli-6: added interruption fix --- cecli/coders/base_coder.py | 37 +++++++++++++++++++++++++++++-------- cecli/io.py | 5 +++++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/cecli/coders/base_coder.py b/cecli/coders/base_coder.py index 4139412156a..05f955f5020 100755 --- a/cecli/coders/base_coder.py +++ b/cecli/coders/base_coder.py @@ -328,6 +328,7 @@ def __init__( uuid="", ): # initialize from args.map_cache_dir + self.interrupt_event = asyncio.Event() self.uuid = generate_unique_id() if uuid: self.uuid = uuid @@ -1735,6 +1736,7 @@ def keyboard_interrupt(self): self.io.tool_warning("\n\n^C KeyboardInterrupt") + self.interrupt_event.set() self.last_keyboard_interrupt = time.time() # Old summarization system removed - using context compaction logic instead @@ -3039,6 +3041,7 @@ async def check_for_file_mentions(self, content): return prompts.added_files.format(fnames=", ".join(added_fnames)) async def send(self, messages, model=None, functions=None, tools=None): + self.interrupt_event.clear() self.got_reasoning_content = False self.ended_reasoning_content = False @@ -3058,15 +3061,33 @@ async def send(self, messages, model=None, functions=None, tools=None): self.token_profiler.start() try: - hash_object, completion = await model.send_completion( - messages, - functions, - self.stream, - self.temperature, - # This could include any tools, but for now it is just MCP tools - tools=tools, - override_kwargs=self.model_kwargs.copy(), + completion_task = asyncio.create_task( + model.send_completion( + messages, + functions, + self.stream, + self.temperature, + # This could include any tools, but for now it is just MCP tools + tools=tools, + override_kwargs=self.model_kwargs.copy(), + ) + ) + interrupt_task = asyncio.create_task(self.interrupt_event.wait()) + + done, pending = await asyncio.wait( + {completion_task, interrupt_task}, + return_when=asyncio.FIRST_COMPLETED, ) + + if interrupt_task in done: + completion_task.cancel() + try: + await completion_task + except asyncio.CancelledError: + pass + raise KeyboardInterrupt + + hash_object, completion = completion_task.result() self.chat_completion_call_hashes.append(hash_object.hexdigest()) if not isinstance(completion, ModelResponse): diff --git a/cecli/io.py b/cecli/io.py index 8f572b7e856..b0789193822 100644 --- a/cecli/io.py +++ b/cecli/io.py @@ -758,6 +758,11 @@ def rule(self): print() def interrupt_input(self): + if self.coder: + coder = self.coder() + if coder and hasattr(coder, "interrupt_event"): + coder.interrupt_event.set() + if self.prompt_session and self.prompt_session.app: # Store any partial input before interrupting self.placeholder = self.prompt_session.app.current_buffer.text