v2.9.5 (#676)
Co-authored-by: Clément Drouin <clement.drouin@mistral.ai> Co-authored-by: Kim-Adeline Miguel <51720070+kimadeline@users.noreply.github.com> Co-authored-by: Mathias Gesbert <mathias.gesbert@mistral.ai> Co-authored-by: allansimon-mistral <allan.simon@ext.mistral.ai> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
parent
4972dd5694
commit
b23f49e5f4
74 changed files with 2342 additions and 240 deletions
|
|
@ -32,7 +32,7 @@ class SlashCommandController:
|
|||
return
|
||||
|
||||
suggestions = self._completer.get_completion_items(text, cursor_index)
|
||||
if suggestions and all(alias != text for alias, _ in suggestions):
|
||||
if suggestions:
|
||||
self._suggestions = suggestions
|
||||
self._selected_index = 0
|
||||
self._view.render_completion_suggestions(
|
||||
|
|
|
|||
|
|
@ -42,10 +42,10 @@ def _build_cli_entrypoint_metadata() -> EntrypointMetadata:
|
|||
)
|
||||
|
||||
|
||||
def get_initial_agent_name(args: argparse.Namespace) -> str:
|
||||
if args.prompt is not None and args.agent == BuiltinAgentName.DEFAULT:
|
||||
def get_initial_agent_name(args: argparse.Namespace, config: VibeConfig) -> str:
|
||||
if args.prompt is not None and args.agent is None:
|
||||
return BuiltinAgentName.AUTO_APPROVE
|
||||
return args.agent
|
||||
return args.agent or config.default_agent
|
||||
|
||||
|
||||
def get_prompt_from_stdin() -> str | None:
|
||||
|
|
@ -196,9 +196,9 @@ def run_cli(args: argparse.Namespace) -> None:
|
|||
sys.exit(0)
|
||||
|
||||
try:
|
||||
initial_agent_name = get_initial_agent_name(args)
|
||||
is_interactive = args.prompt is None
|
||||
config = load_config_or_exit(interactive=is_interactive)
|
||||
initial_agent_name = get_initial_agent_name(args, config)
|
||||
hook_config_result = load_hooks_from_fs(config)
|
||||
setup_tracing(config)
|
||||
|
||||
|
|
@ -243,18 +243,22 @@ def run_cli(args: argparse.Namespace) -> None:
|
|||
except TeleportError as e:
|
||||
print(f"Teleport error: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
except RuntimeError as e:
|
||||
except (RuntimeError, ValueError) as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
else:
|
||||
agent_loop = AgentLoop(
|
||||
config,
|
||||
agent_name=initial_agent_name,
|
||||
enable_streaming=True,
|
||||
entrypoint_metadata=_build_cli_entrypoint_metadata(),
|
||||
defer_heavy_init=True,
|
||||
hook_config_result=hook_config_result,
|
||||
)
|
||||
try:
|
||||
agent_loop = AgentLoop(
|
||||
config,
|
||||
agent_name=initial_agent_name,
|
||||
enable_streaming=True,
|
||||
entrypoint_metadata=_build_cli_entrypoint_metadata(),
|
||||
defer_heavy_init=True,
|
||||
hook_config_result=hook_config_result,
|
||||
)
|
||||
except ValueError as e:
|
||||
rprint(f"[red]Error:[/] {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if loaded_session:
|
||||
_resume_previous_session(agent_loop, *loaded_session)
|
||||
|
|
|
|||
|
|
@ -161,6 +161,14 @@ class CommandRegistry:
|
|||
description="Rewind to a previous message",
|
||||
handler="_start_rewind_mode",
|
||||
),
|
||||
"loop": Command(
|
||||
aliases=frozenset(["/loop"]),
|
||||
description=(
|
||||
"Schedule a recurring prompt. "
|
||||
"Use `/loop <interval> <prompt>`, `/loop list`, or `/loop cancel <id|all>`"
|
||||
),
|
||||
handler="_loop_command",
|
||||
),
|
||||
"data-retention": Command(
|
||||
aliases=frozenset(["/data-retention"]),
|
||||
description="Show data retention information",
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import sys
|
|||
from rich import print as rprint
|
||||
|
||||
from vibe import __version__
|
||||
from vibe.core.agents.models import BuiltinAgentName
|
||||
from vibe.core.config.harness_files import init_harness_files_manager
|
||||
from vibe.core.trusted_folders import find_trustable_files, trusted_folders_manager
|
||||
from vibe.setup.trusted_folders.trust_folder_dialog import (
|
||||
|
|
@ -18,7 +17,18 @@ from vibe.setup.trusted_folders.trust_folder_dialog import (
|
|||
|
||||
|
||||
def parse_arguments() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Run the Mistral Vibe interactive CLI")
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Run the Mistral Vibe interactive CLI",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog=(
|
||||
"Environment variables:\n"
|
||||
" VIBE_HOME Override the Vibe home directory (default: ~/.vibe)\n"
|
||||
" LOG_LEVEL Logging level: DEBUG, INFO, WARNING (default), ERROR, CRITICAL.\n"
|
||||
" Logs are written to $VIBE_HOME/logs/vibe.log.\n"
|
||||
" LOG_MAX_BYTES Max size of vibe.log before rotation (default: 10485760).\n"
|
||||
" VIBE_* Override any config field (e.g. VIBE_ACTIVE_MODEL=local)."
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v", "--version", action="version", version=f"%(prog)s {__version__}"
|
||||
)
|
||||
|
|
@ -72,9 +82,12 @@ def parse_arguments() -> argparse.Namespace:
|
|||
parser.add_argument(
|
||||
"--agent",
|
||||
metavar="NAME",
|
||||
default=BuiltinAgentName.DEFAULT,
|
||||
default=None,
|
||||
help="Agent to use (builtin: default, plan, accept-edits, auto-approve, "
|
||||
"or custom from ~/.vibe/agents/NAME.toml)",
|
||||
"or custom from ~/.vibe/agents/NAME.toml). In interactive mode, "
|
||||
"defaults to the 'default_agent' config setting. In programmatic "
|
||||
"mode (-p/--prompt), defaults to auto-approve and 'default_agent' "
|
||||
"is ignored.",
|
||||
)
|
||||
parser.add_argument("--setup", action="store_true", help="Setup API key and exit")
|
||||
parser.add_argument(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
|
||||
from vibe.core.utils.io import read_safe
|
||||
|
||||
|
|
@ -22,12 +24,10 @@ class HistoryManager:
|
|||
try:
|
||||
text = read_safe(self.history_file).text
|
||||
except OSError:
|
||||
self._entries = []
|
||||
return
|
||||
|
||||
entries = []
|
||||
for raw_line in text.splitlines():
|
||||
raw_line = raw_line.rstrip("\n\r")
|
||||
if not raw_line:
|
||||
continue
|
||||
try:
|
||||
|
|
@ -36,13 +36,24 @@ class HistoryManager:
|
|||
entry = raw_line
|
||||
entries.append(entry if isinstance(entry, str) else str(entry))
|
||||
self._entries = entries[-self.max_entries :]
|
||||
self.reset_navigation()
|
||||
|
||||
def _save_history(self) -> None:
|
||||
try:
|
||||
self.history_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
with self.history_file.open("w", encoding="utf-8") as f:
|
||||
for entry in self._entries:
|
||||
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
||||
fd, tmp_path = tempfile.mkstemp(
|
||||
prefix=f".{self.history_file.name}.",
|
||||
suffix=".tmp",
|
||||
dir=self.history_file.parent,
|
||||
)
|
||||
try:
|
||||
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
||||
for entry in self._entries:
|
||||
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
||||
os.replace(tmp_path, self.history_file)
|
||||
except OSError:
|
||||
Path(tmp_path).unlink(missing_ok=True)
|
||||
raise
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
|
@ -51,6 +62,8 @@ class HistoryManager:
|
|||
if not text:
|
||||
return
|
||||
|
||||
self._load_history()
|
||||
|
||||
if self._entries and self._entries[-1] == text:
|
||||
return
|
||||
|
||||
|
|
@ -60,7 +73,6 @@ class HistoryManager:
|
|||
self._entries = self._entries[-self.max_entries :]
|
||||
|
||||
self._save_history()
|
||||
self.reset_navigation()
|
||||
|
||||
def get_previous(self, current_input: str) -> str | None:
|
||||
if not self._entries:
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ from vibe.cli.textual_ui.notifications import (
|
|||
)
|
||||
from vibe.cli.textual_ui.quit_manager import QuitManager
|
||||
from vibe.cli.textual_ui.remote import RemoteSessionManager, is_progress_event
|
||||
from vibe.cli.textual_ui.scheduled_loop_runner import ScheduledLoopRunner
|
||||
from vibe.cli.textual_ui.session_exit import print_session_resume_message
|
||||
from vibe.cli.textual_ui.widgets.approval_app import ApprovalApp
|
||||
from vibe.cli.textual_ui.widgets.banner.banner import Banner
|
||||
|
|
@ -140,6 +141,7 @@ from vibe.core.session.resume_sessions import (
|
|||
from vibe.core.session.saved_sessions import update_saved_session_title_at_path
|
||||
from vibe.core.session.session_loader import SessionLoader
|
||||
from vibe.core.skills.manager import SkillManager
|
||||
from vibe.core.teleport.telemetry import send_teleport_early_failure_telemetry
|
||||
from vibe.core.teleport.types import (
|
||||
TeleportAuthCompleteEvent,
|
||||
TeleportAuthRequiredEvent,
|
||||
|
|
@ -339,7 +341,6 @@ class VibeApp(App): # noqa: PLR0904
|
|||
**kwargs: Any,
|
||||
) -> None:
|
||||
super().__init__(**kwargs)
|
||||
self.scroll_sensitivity_y = 1.0
|
||||
self.agent_loop = agent_loop
|
||||
self._plan_info: PlanInfo | None = None
|
||||
self._voice_manager: VoiceManagerPort = (
|
||||
|
|
@ -397,7 +398,17 @@ class VibeApp(App): # noqa: PLR0904
|
|||
self._rewind_mode = False
|
||||
self._rewind_highlighted_widget: UserMessage | None = None
|
||||
self._fatal_init_error = False
|
||||
self._force_quit_task: asyncio.Task[None] | None = None
|
||||
self.commands = self._build_command_registry()
|
||||
self._loop_runner = ScheduledLoopRunner(
|
||||
self.agent_loop.session_logger,
|
||||
can_fire=lambda: (
|
||||
not self._agent_running and self._current_bottom_app == BottomApp.Input
|
||||
),
|
||||
fire=self._handle_user_message,
|
||||
mount=self._mount_and_scroll,
|
||||
tools_collapsed=lambda: self._tools_collapsed,
|
||||
)
|
||||
|
||||
def _configure_startup_options(self, startup: StartupOptions | None) -> None:
|
||||
opts = startup or StartupOptions()
|
||||
|
|
@ -504,6 +515,8 @@ class VibeApp(App): # noqa: PLR0904
|
|||
await self._resolve_plan()
|
||||
await self._show_dangerous_directory_warning()
|
||||
await self._resume_history_from_messages()
|
||||
self._loop_runner.restore_from_session()
|
||||
self._loop_runner.start()
|
||||
await self._check_and_show_whats_new()
|
||||
self._schedule_update_notification()
|
||||
if not self._is_resuming_session:
|
||||
|
|
@ -1407,6 +1420,12 @@ class VibeApp(App): # noqa: PLR0904
|
|||
if show_message:
|
||||
await self._mount_and_scroll(UserMessage("/teleport"))
|
||||
if not has_history:
|
||||
send_teleport_early_failure_telemetry(
|
||||
self.agent_loop.telemetry_client,
|
||||
stage="no_history",
|
||||
error_class="TeleportNoHistoryError",
|
||||
nb_session_messages=len(self.agent_loop.messages[1:]),
|
||||
)
|
||||
await self._mount_and_scroll(
|
||||
ErrorMessage(
|
||||
"No conversation history to teleport.",
|
||||
|
|
@ -1429,6 +1448,12 @@ class VibeApp(App): # noqa: PLR0904
|
|||
await self._mount_and_scroll(teleport_msg)
|
||||
|
||||
if self._remote_manager.is_active:
|
||||
send_teleport_early_failure_telemetry(
|
||||
self.agent_loop.telemetry_client,
|
||||
stage="remote_session",
|
||||
error_class="TeleportRemoteSessionError",
|
||||
nb_session_messages=len(self.agent_loop.messages[1:]),
|
||||
)
|
||||
await loading.remove()
|
||||
await self._mount_and_scroll(
|
||||
ErrorMessage(
|
||||
|
|
@ -1818,6 +1843,8 @@ class VibeApp(App): # noqa: PLR0904
|
|||
f"Session `{short_session_id(session.session_id)}` not found."
|
||||
)
|
||||
|
||||
self._emit_session_closed_for_active_session()
|
||||
|
||||
loaded_messages, metadata = SessionLoader.load_session(session_path)
|
||||
if self._chat_input_container:
|
||||
self._chat_input_container.set_custom_border(None)
|
||||
|
|
@ -1846,6 +1873,7 @@ class VibeApp(App): # noqa: PLR0904
|
|||
if self.event_handler:
|
||||
self.event_handler.is_remote = False
|
||||
await self._resume_history_from_messages()
|
||||
self._loop_runner.restore_from_session()
|
||||
await self._mount_and_scroll(
|
||||
UserCommandMessage(
|
||||
f"Resumed session `{short_session_id(session.session_id)}`"
|
||||
|
|
@ -1856,6 +1884,8 @@ class VibeApp(App): # noqa: PLR0904
|
|||
await self._remote_manager.attach(
|
||||
session_id=session.session_id, config=self.config
|
||||
)
|
||||
self._emit_session_closed_for_active_session()
|
||||
self.agent_loop.session_id = session.session_id
|
||||
self._refresh_profile_widgets()
|
||||
if self._chat_input_container:
|
||||
self._chat_input_container.set_custom_border(None)
|
||||
|
|
@ -2018,6 +2048,10 @@ class VibeApp(App): # noqa: PLR0904
|
|||
)
|
||||
)
|
||||
|
||||
async def _loop_command(self, cmd_args: str = "", **kwargs: Any) -> None:
|
||||
widget = await self._loop_runner.handle_command(cmd_args)
|
||||
await self._mount_and_scroll(widget)
|
||||
|
||||
async def _compact_history(self, cmd_args: str = "", **kwargs: Any) -> None:
|
||||
if self._agent_running:
|
||||
await self._mount_and_scroll(
|
||||
|
|
@ -2041,22 +2075,32 @@ class VibeApp(App): # noqa: PLR0904
|
|||
return
|
||||
|
||||
old_tokens = self.agent_loop.stats.context_tokens
|
||||
old_session_id = self.agent_loop.session_id
|
||||
compact_msg = CompactMessage()
|
||||
self.event_handler.current_compact = compact_msg
|
||||
await self._mount_and_scroll(compact_msg)
|
||||
|
||||
self._agent_task = asyncio.create_task(
|
||||
self._run_compact(compact_msg, old_tokens, cmd_args.strip())
|
||||
self._run_compact(compact_msg, old_tokens, old_session_id, cmd_args.strip())
|
||||
)
|
||||
|
||||
async def _run_compact(
|
||||
self, compact_msg: CompactMessage, old_tokens: int, extra_instructions: str = ""
|
||||
self,
|
||||
compact_msg: CompactMessage,
|
||||
old_tokens: int,
|
||||
old_session_id: str,
|
||||
extra_instructions: str = "",
|
||||
) -> None:
|
||||
self._agent_running = True
|
||||
try:
|
||||
await self.agent_loop.compact(extra_instructions=extra_instructions)
|
||||
new_tokens = self.agent_loop.stats.context_tokens
|
||||
compact_msg.set_complete(old_tokens=old_tokens, new_tokens=new_tokens)
|
||||
compact_msg.set_complete(
|
||||
old_tokens=old_tokens,
|
||||
new_tokens=new_tokens,
|
||||
old_session_id=old_session_id,
|
||||
new_session_id=self.agent_loop.session_id,
|
||||
)
|
||||
|
||||
except asyncio.CancelledError:
|
||||
compact_msg.set_error("Compaction interrupted")
|
||||
|
|
@ -2085,9 +2129,16 @@ class VibeApp(App): # noqa: PLR0904
|
|||
return short_session_id(self.agent_loop.session_logger.session_id)
|
||||
|
||||
async def _exit_app(self, **kwargs: Any) -> None:
|
||||
self._emit_session_closed_for_active_session()
|
||||
await self._loop_runner.stop()
|
||||
self._log_reader.shutdown()
|
||||
await self._narrator_manager.close()
|
||||
self.exit(result=self._get_session_resume_info())
|
||||
try:
|
||||
await self.agent_loop.telemetry_client.aclose()
|
||||
except Exception as exc:
|
||||
logger.error("Failed to close telemetry client during exit", exc_info=exc)
|
||||
finally:
|
||||
self.exit(result=self._get_session_resume_info())
|
||||
|
||||
def _make_default_voice_manager(self) -> VoiceManager:
|
||||
try:
|
||||
|
|
@ -2746,7 +2797,16 @@ class VibeApp(App): # noqa: PLR0904
|
|||
return
|
||||
self._quit_manager.request_confirmation("Ctrl+D")
|
||||
|
||||
def _emit_session_closed_for_active_session(self) -> None:
|
||||
self.agent_loop.emit_session_closed_telemetry()
|
||||
|
||||
def _force_quit(self) -> None:
|
||||
if self._force_quit_task is not None and not self._force_quit_task.done():
|
||||
return
|
||||
self._force_quit_task = asyncio.create_task(self._force_quit_async())
|
||||
|
||||
async def _force_quit_async(self) -> None:
|
||||
self._emit_session_closed_for_active_session()
|
||||
if self._agent_task and not self._agent_task.done():
|
||||
self._agent_task.cancel()
|
||||
if self._bash_task and not self._bash_task.done():
|
||||
|
|
@ -2755,7 +2815,14 @@ class VibeApp(App): # noqa: PLR0904
|
|||
|
||||
self._log_reader.shutdown()
|
||||
self._narrator_manager.cancel()
|
||||
self.exit(result=self._get_session_resume_info())
|
||||
try:
|
||||
await self.agent_loop.telemetry_client.aclose()
|
||||
except Exception as exc:
|
||||
logger.error(
|
||||
"Failed to close telemetry client during force quit", exc_info=exc
|
||||
)
|
||||
finally:
|
||||
self.exit(result=self._get_session_resume_info())
|
||||
|
||||
def action_scroll_chat_up(self) -> None:
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -980,7 +980,7 @@ NarratorStatus {
|
|||
height: auto;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
margin: 0 0 0 1;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#banner-container {
|
||||
|
|
|
|||
|
|
@ -217,7 +217,10 @@ class EventHandler:
|
|||
async def _handle_compact_end(self, event: CompactEndEvent) -> None:
|
||||
if self.current_compact:
|
||||
self.current_compact.set_complete(
|
||||
old_tokens=event.old_context_tokens, new_tokens=event.new_context_tokens
|
||||
old_tokens=event.old_context_tokens,
|
||||
new_tokens=event.new_context_tokens,
|
||||
old_session_id=event.old_session_id,
|
||||
new_session_id=event.new_session_id,
|
||||
)
|
||||
self.current_compact = None
|
||||
|
||||
|
|
|
|||
99
vibe/cli/textual_ui/scheduled_loop_runner.py
Normal file
99
vibe/cli/textual_ui/scheduled_loop_runner.py
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Awaitable, Callable
|
||||
import time
|
||||
|
||||
from textual.widget import Widget
|
||||
|
||||
from vibe.cli.textual_ui.widgets.messages import ErrorMessage, UserCommandMessage
|
||||
from vibe.core.logger import logger
|
||||
from vibe.core.loop import (
|
||||
USAGE_HINT,
|
||||
LoopErrorResult,
|
||||
LoopListResult,
|
||||
LoopManager,
|
||||
LoopOkResult,
|
||||
ScheduledLoop,
|
||||
format_duration,
|
||||
)
|
||||
from vibe.core.session.session_logger import SessionLogger
|
||||
|
||||
|
||||
def _format_loop_list(loops: list[ScheduledLoop]) -> str:
|
||||
if not loops:
|
||||
return "No scheduled loops."
|
||||
now = time.time()
|
||||
rows = ["| Prompt | Next in | Every | ID |", "|--------|------|-------|----|"]
|
||||
for loop in loops:
|
||||
remaining = format_duration(max(0, int(loop.next_fire_at - now)), short=True)
|
||||
interval = format_duration(loop.interval_seconds)
|
||||
prompt = loop.prompt.replace("|", "\\|").replace("\n", " ")
|
||||
rows.append(f"| {prompt} | {remaining} | {interval} | `{loop.id}` |")
|
||||
return "\n".join(rows)
|
||||
|
||||
|
||||
class ScheduledLoopRunner:
|
||||
def __init__(
|
||||
self,
|
||||
session_logger: SessionLogger,
|
||||
*,
|
||||
can_fire: Callable[[], bool],
|
||||
fire: Callable[[str], Awaitable[None]],
|
||||
mount: Callable[[Widget], Awaitable[None]],
|
||||
tools_collapsed: Callable[[], bool],
|
||||
) -> None:
|
||||
self._session_logger = session_logger
|
||||
self._manager = LoopManager(session_logger)
|
||||
self._can_fire = can_fire
|
||||
self._fire = fire
|
||||
self._mount = mount
|
||||
self._tools_collapsed = tools_collapsed
|
||||
self._task: asyncio.Task[None] | None = None
|
||||
|
||||
def restore_from_session(self) -> None:
|
||||
metadata = self._session_logger.session_metadata
|
||||
self._manager.restore(list(metadata.loops) if metadata is not None else [])
|
||||
|
||||
def start(self) -> None:
|
||||
if self._task is None or self._task.done():
|
||||
self._task = asyncio.create_task(self._poll())
|
||||
|
||||
async def stop(self) -> None:
|
||||
if self._task is None:
|
||||
return
|
||||
self._task.cancel()
|
||||
try:
|
||||
await self._task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
self._task = None
|
||||
|
||||
async def handle_command(self, cmd_args: str) -> Widget:
|
||||
result = await self._manager.handle_command(cmd_args)
|
||||
match result:
|
||||
case LoopListResult(loops=loops):
|
||||
return UserCommandMessage(_format_loop_list(loops))
|
||||
case LoopErrorResult(message=message):
|
||||
return ErrorMessage(
|
||||
f"{message}\n{USAGE_HINT}", collapsed=self._tools_collapsed()
|
||||
)
|
||||
case LoopOkResult(message=message):
|
||||
return UserCommandMessage(message)
|
||||
|
||||
async def _poll(self) -> None:
|
||||
while True:
|
||||
try:
|
||||
sleep_for = min(self._manager.next_due_in(), 1.0)
|
||||
await asyncio.sleep(max(0.05, sleep_for))
|
||||
if not self._can_fire():
|
||||
continue
|
||||
due = await self._manager.pop_due()
|
||||
if due is None:
|
||||
continue
|
||||
await self._fire(due.prompt)
|
||||
await self._mount(UserCommandMessage(f"Loop `{due.id}` fired"))
|
||||
except asyncio.CancelledError:
|
||||
return
|
||||
except Exception as e:
|
||||
logger.error("Error polling scheduled loops", exc_info=e)
|
||||
|
|
@ -17,6 +17,8 @@ class CompactMessage(StatusMessage):
|
|||
self.add_class("compact-message")
|
||||
self.old_tokens: int | None = None
|
||||
self.new_tokens: int | None = None
|
||||
self.old_session_id: str | None = None
|
||||
self.new_session_id: str | None = None
|
||||
self.error_message: str | None = None
|
||||
|
||||
def get_content(self) -> str:
|
||||
|
|
@ -26,13 +28,25 @@ class CompactMessage(StatusMessage):
|
|||
if self.error_message:
|
||||
return f"Error: {self.error_message}"
|
||||
|
||||
return compact_reduction_display(self.old_tokens, self.new_tokens)
|
||||
return compact_reduction_display(
|
||||
self.old_tokens,
|
||||
self.new_tokens,
|
||||
old_session_id=self.old_session_id,
|
||||
new_session_id=self.new_session_id,
|
||||
)
|
||||
|
||||
def set_complete(
|
||||
self, old_tokens: int | None = None, new_tokens: int | None = None
|
||||
self,
|
||||
old_tokens: int | None = None,
|
||||
new_tokens: int | None = None,
|
||||
*,
|
||||
old_session_id: str | None = None,
|
||||
new_session_id: str | None = None,
|
||||
) -> None:
|
||||
self.old_tokens = old_tokens
|
||||
self.new_tokens = new_tokens
|
||||
self.old_session_id = old_session_id
|
||||
self.new_session_id = new_session_id
|
||||
self.stop_spinning(success=True)
|
||||
self.post_message(self.Completed(self))
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue