v2.9.6 (#682)
Co-authored-by: Clément Drouin <clement.drouin@mistral.ai> Co-authored-by: Michel Thomazo <51709227+michelTho@users.noreply.github.com> Co-authored-by: Pierre Rossinès <pierre.rossines@mistral.ai> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
parent
b23f49e5f4
commit
626f905186
59 changed files with 702 additions and 183 deletions
|
|
@ -210,7 +210,11 @@ def run_cli(args: argparse.Namespace) -> None:
|
|||
stdin_prompt = get_prompt_from_stdin()
|
||||
if args.prompt is not None:
|
||||
warn_if_workdir_trust_is_unset()
|
||||
config.disabled_tools = [*config.disabled_tools, "ask_user_question"]
|
||||
config.disabled_tools = [
|
||||
*config.disabled_tools,
|
||||
"ask_user_question",
|
||||
"exit_plan_mode",
|
||||
]
|
||||
programmatic_prompt = args.prompt or stdin_prompt
|
||||
if not programmatic_prompt:
|
||||
print(
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from vibe.cli.plan_offer.ports.whoami_gateway import (
|
|||
WhoAmIGatewayUnauthorized,
|
||||
WhoAmIResponse,
|
||||
)
|
||||
from vibe.core.utils.http import build_ssl_context
|
||||
|
||||
BASE_URL = "https://console.mistral.ai"
|
||||
WHOAMI_PATH = "/api/vibe/whoami"
|
||||
|
|
@ -23,7 +24,7 @@ class HttpWhoAmIGateway:
|
|||
url = f"{self._base_url}{WHOAMI_PATH}"
|
||||
headers = {"Authorization": f"Bearer {api_key}"}
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
async with httpx.AsyncClient(verify=build_ssl_context()) as client:
|
||||
response = await client.get(url, headers=headers)
|
||||
except httpx.RequestError as exc:
|
||||
raise WhoAmIGatewayError() from exc
|
||||
|
|
|
|||
|
|
@ -987,6 +987,8 @@ class VibeApp(App): # noqa: PLR0904
|
|||
|
||||
bash_msg = BashOutputMessage(command, str(Path.cwd()), pending=True)
|
||||
await self._mount_and_scroll(bash_msg)
|
||||
await self._ensure_loading_widget("Running command")
|
||||
bash_loading_widget = self._loading_widget
|
||||
|
||||
proc: asyncio.subprocess.Process | None = None
|
||||
stdout_parts: list[str] = []
|
||||
|
|
@ -1071,6 +1073,9 @@ class VibeApp(App): # noqa: PLR0904
|
|||
status=f"failed before completion: {e}",
|
||||
)
|
||||
)
|
||||
finally:
|
||||
if self._loading_widget is bash_loading_widget:
|
||||
await self._remove_loading_widget()
|
||||
|
||||
def _get_bash_max_output_bytes(self) -> int:
|
||||
from vibe.core.tools.builtins.bash import BashToolConfig
|
||||
|
|
@ -2132,7 +2137,9 @@ class VibeApp(App): # noqa: PLR0904
|
|||
self._emit_session_closed_for_active_session()
|
||||
await self._loop_runner.stop()
|
||||
self._log_reader.shutdown()
|
||||
await self._voice_manager.close()
|
||||
await self._narrator_manager.close()
|
||||
await self.agent_loop.aclose()
|
||||
try:
|
||||
await self.agent_loop.telemetry_client.aclose()
|
||||
except Exception as exc:
|
||||
|
|
@ -2629,6 +2636,9 @@ class VibeApp(App): # noqa: PLR0904
|
|||
return True
|
||||
|
||||
interrupted = False
|
||||
if self._bash_task and not self._bash_task.done():
|
||||
self._bash_task.cancel()
|
||||
interrupted = True
|
||||
if self._agent_running:
|
||||
self._handle_agent_running_escape()
|
||||
interrupted = True
|
||||
|
|
@ -2815,6 +2825,7 @@ class VibeApp(App): # noqa: PLR0904
|
|||
|
||||
self._log_reader.shutdown()
|
||||
self._narrator_manager.cancel()
|
||||
await self.agent_loop.aclose()
|
||||
try:
|
||||
await self.agent_loop.telemetry_client.aclose()
|
||||
except Exception as exc:
|
||||
|
|
|
|||
|
|
@ -406,6 +406,10 @@ Markdown {
|
|||
width: auto;
|
||||
height: auto;
|
||||
|
||||
&.bash-pending {
|
||||
color: $foreground;
|
||||
}
|
||||
|
||||
&.bash-success {
|
||||
color: ansi_green;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -263,7 +263,9 @@ class InterruptMessage(Static):
|
|||
)
|
||||
|
||||
|
||||
class BashOutputMessage(Static):
|
||||
class BashOutputMessage(SpinnerMixin, Static):
|
||||
SPINNER_TYPE = SpinnerType.PULSE
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
command: str,
|
||||
|
|
@ -274,6 +276,7 @@ class BashOutputMessage(Static):
|
|||
pending: bool = False,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.init_spinner()
|
||||
self.add_class("bash-output-message")
|
||||
self._command = command
|
||||
self._cwd = cwd
|
||||
|
|
@ -283,17 +286,29 @@ class BashOutputMessage(Static):
|
|||
self._output_widget: NoMarkupStatic | None = None
|
||||
self._output_container: Horizontal | None = None
|
||||
self._prompt_widget: NonSelectableStatic | None = None
|
||||
self._indicator_widget: Static | None = None
|
||||
|
||||
def _update_spinner_frame(self) -> None:
|
||||
if not self._is_spinning or not self._prompt_widget:
|
||||
return
|
||||
self._prompt_widget.update(f"{self._spinner.next_frame()} ")
|
||||
|
||||
def on_mount(self) -> None:
|
||||
if self._pending:
|
||||
self.start_spinner_timer()
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
status_class = (
|
||||
"bash-error"
|
||||
if not self._pending and self._exit_code != 0
|
||||
else "bash-success"
|
||||
)
|
||||
if self._pending:
|
||||
status_class = "bash-pending"
|
||||
elif self._exit_code != 0:
|
||||
status_class = "bash-error"
|
||||
else:
|
||||
status_class = "bash-success"
|
||||
self.add_class(status_class)
|
||||
prompt_text = f"{self._spinner.current_frame()} " if self._pending else "$ "
|
||||
with Horizontal(classes="bash-command-line"):
|
||||
self._prompt_widget = NonSelectableStatic(
|
||||
"$ ", classes=f"bash-prompt {status_class}"
|
||||
prompt_text, classes=f"bash-prompt {status_class}"
|
||||
)
|
||||
yield self._prompt_widget
|
||||
yield NoMarkupStatic(self._command, classes="bash-command")
|
||||
|
|
@ -326,18 +341,20 @@ class BashOutputMessage(Static):
|
|||
async def finish(self, exit_code: int, *, interrupted: bool = False) -> None:
|
||||
self._exit_code = exit_code
|
||||
self._pending = False
|
||||
self.stop_spinning()
|
||||
if self._prompt_widget:
|
||||
self._prompt_widget.update("$ ")
|
||||
if interrupted:
|
||||
self.remove_class("bash-success")
|
||||
self.add_class("bash-interrupted")
|
||||
if self._prompt_widget:
|
||||
self._prompt_widget.remove_class("bash-success")
|
||||
self._prompt_widget.add_class("bash-interrupted")
|
||||
new_class = "bash-interrupted"
|
||||
elif exit_code != 0:
|
||||
self.remove_class("bash-success")
|
||||
self.add_class("bash-error")
|
||||
if self._prompt_widget:
|
||||
self._prompt_widget.remove_class("bash-success")
|
||||
self._prompt_widget.add_class("bash-error")
|
||||
new_class = "bash-error"
|
||||
else:
|
||||
new_class = "bash-success"
|
||||
self.remove_class("bash-pending")
|
||||
self.add_class(new_class)
|
||||
if self._prompt_widget:
|
||||
self._prompt_widget.remove_class("bash-pending")
|
||||
self._prompt_widget.add_class(new_class)
|
||||
if interrupted:
|
||||
suffix = (
|
||||
"\n(interrupted)"
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ from vibe.cli.update_notifier.ports.update_gateway import (
|
|||
UpdateGatewayCause,
|
||||
UpdateGatewayError,
|
||||
)
|
||||
from vibe.core.utils.http import build_ssl_context
|
||||
|
||||
|
||||
class GitHubUpdateGateway(UpdateGateway):
|
||||
|
|
@ -47,7 +48,9 @@ class GitHubUpdateGateway(UpdateGateway):
|
|||
)
|
||||
else:
|
||||
async with httpx.AsyncClient(
|
||||
base_url=self._base_url, timeout=self._timeout
|
||||
base_url=self._base_url,
|
||||
timeout=self._timeout,
|
||||
verify=build_ssl_context(),
|
||||
) as client:
|
||||
response = await client.get(request_path, headers=headers)
|
||||
except httpx.RequestError as exc:
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from vibe.cli.update_notifier.ports.update_gateway import (
|
|||
UpdateGatewayCause,
|
||||
UpdateGatewayError,
|
||||
)
|
||||
from vibe.core.utils.http import build_ssl_context
|
||||
|
||||
_STATUS_CAUSES: dict[int, UpdateGatewayCause] = {
|
||||
httpx.codes.NOT_FOUND: UpdateGatewayCause.NOT_FOUND,
|
||||
|
|
@ -81,7 +82,9 @@ class PyPIUpdateGateway(UpdateGateway):
|
|||
)
|
||||
|
||||
async with httpx.AsyncClient(
|
||||
base_url=self._base_url, timeout=self._timeout
|
||||
base_url=self._base_url,
|
||||
timeout=self._timeout,
|
||||
verify=build_ssl_context(),
|
||||
) as client:
|
||||
return await client.get(request_path, headers=headers)
|
||||
except httpx.RequestError as exc:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from asyncio import CancelledError, create_task, wait_for
|
||||
import contextlib
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from vibe.cli.voice_manager.telemetry import TranscriptionTrackingState
|
||||
|
|
@ -154,6 +155,15 @@ class VoiceManager:
|
|||
except ValueError:
|
||||
pass
|
||||
|
||||
async def close(self) -> None:
|
||||
transcribe_task = self._transcribe_task
|
||||
self.cancel_recording()
|
||||
if transcribe_task is not None:
|
||||
with contextlib.suppress(CancelledError):
|
||||
await transcribe_task
|
||||
if self._transcribe_client is not None:
|
||||
await self._transcribe_client.close()
|
||||
|
||||
async def _run_transcription(self) -> None:
|
||||
if self._transcribe_client is None:
|
||||
return
|
||||
|
|
|
|||
|
|
@ -54,3 +54,5 @@ class VoiceManagerPort(Protocol):
|
|||
def add_listener(self, listener: VoiceManagerListener) -> None: ...
|
||||
|
||||
def remove_listener(self, listener: VoiceManagerListener) -> None: ...
|
||||
|
||||
async def close(self) -> None: ...
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue