Skip to content

Commit 0751fc2

Browse files
authored
Update commands.py
1 parent 27d2848 commit 0751fc2

1 file changed

Lines changed: 72 additions & 40 deletions

File tree

‎python_agent_harness/tui/commands.py‎

Lines changed: 72 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -262,9 +262,74 @@ def _conversation_text(self) -> str:
262262
# ------------------------------------------------------------------
263263
# direct commands (no LLM agent loop)
264264
# ------------------------------------------------------------------
265+
def _run_with_status(
266+
self,
267+
worker: Callable[[], None],
268+
*,
269+
status_text: str,
270+
cancel_message: str,
271+
) -> None:
272+
"""Run *worker* in a background thread with status bar updates.
273+
274+
Sets the status bar to *status_text*, renders the spinner while
275+
the worker is in flight, and handles KeyboardInterrupt with
276+
*cancel_message*. Used by /compact and /summary to avoid
277+
duplicating the thread + Live boilerplate.
278+
"""
279+
self.status = status_text
280+
self._current_tool = ""
281+
self.agent_running = True
282+
self._data_event.clear()
283+
284+
thread = threading.Thread(target=worker, daemon=True)
285+
thread.start()
286+
try:
287+
if self.console.is_dumb_terminal:
288+
while thread.is_alive():
289+
self._data_event.wait(timeout=0.1)
290+
self._data_event.clear()
291+
self.console.print(self._status_bar())
292+
self._flush()
293+
else:
294+
with Live(
295+
self._status_bar(),
296+
console=self.console,
297+
refresh_per_second=30,
298+
screen=False,
299+
) as live:
300+
while thread.is_alive():
301+
self._data_event.wait(timeout=0.1)
302+
self._data_event.clear()
303+
live.update(self._status_bar())
304+
self._flush()
305+
except KeyboardInterrupt:
306+
self.console.print(f"\n[dim]{cancel_message}[/dim]")
307+
self._flush()
308+
finally:
309+
self.agent_running = False
310+
self.status = ""
311+
self._data_event.set()
312+
265313
def _run_compact(self) -> None:
266314
"""Compact the current conversation directly."""
267-
ok, msg = self.session.compact_conversation()
315+
result: dict[str, Any] = {}
316+
317+
def worker() -> None:
318+
try:
319+
ok, msg = self.session.compact_conversation()
320+
result["ok"] = ok
321+
result["msg"] = msg
322+
except Exception as e: # noqa: BLE001 - surfaced to the user
323+
result["ok"] = False
324+
result["msg"] = f"Compaction failed: {e}"
325+
326+
self._run_with_status(
327+
worker,
328+
status_text=" ⏳ compacting",
329+
cancel_message="compact cancelled — the result may still be applied",
330+
)
331+
ok = result.get("ok", False)
332+
msg = result.get("msg", "Compaction failed: unknown error.")
268333
if ok:
269334
# The shared conversation was replaced: sync the TUI's own
270335
# history too, or the next run would restart from the old
@@ -394,17 +459,7 @@ def _run_model_command(self, arg: str) -> None:
394459
self._model_switch_by_name(arg)
395460

396461
def _run_summary(self) -> None:
397-
"""Append a summary of the conversation (tools disabled).
398-
399-
The summary request is synchronous (non-streaming), so it runs
400-
in a worker thread like the agent loop: the main thread keeps
401-
rendering the status bar / spinner while the request is in
402-
flight, and the result is printed when it lands.
403-
"""
404-
self.status = " ⏳ summarizing"
405-
self._current_tool = ""
406-
self.agent_running = True
407-
self._data_event.clear()
462+
"""Append a summary of the conversation (tools disabled)."""
408463
result: dict[str, str] = {}
409464

410465
def worker() -> None:
@@ -413,34 +468,11 @@ def worker() -> None:
413468
except Exception as e: # noqa: BLE001 - surfaced to the user
414469
result["msg"] = f"Summary failed: {e}"
415470

416-
thread = threading.Thread(target=worker, daemon=True)
417-
thread.start()
418-
try:
419-
if self.console.is_dumb_terminal:
420-
while thread.is_alive():
421-
self._data_event.wait(timeout=0.1)
422-
self._data_event.clear()
423-
self.console.print(self._status_bar())
424-
self._flush()
425-
else:
426-
with Live(
427-
self._status_bar(),
428-
console=self.console,
429-
refresh_per_second=30,
430-
screen=False,
431-
) as live:
432-
while thread.is_alive():
433-
self._data_event.wait(timeout=0.1)
434-
self._data_event.clear()
435-
live.update(self._status_bar())
436-
self._flush()
437-
except KeyboardInterrupt:
438-
self.console.print("\n[dim]summary cancelled — the result may still be appended[/dim]")
439-
self._flush()
440-
finally:
441-
self.agent_running = False
442-
self.status = ""
443-
self._data_event.set()
471+
self._run_with_status(
472+
worker,
473+
status_text=" ⏳ summarizing",
474+
cancel_message="summary cancelled — the result may still be appended",
475+
)
444476
msg = result.get("msg", "Summary failed: unknown error.")
445477
self.conversation_history = list(self.session.last_messages)
446478
self._history_dirty = True

0 commit comments

Comments
 (0)