Co-authored-by: Kim-Adeline Miguel <kimadeline.miguel@mistral.ai>
Co-authored-by: Clement Sirieix <clem.sirieix@gmail.com>
Co-authored-by: laurens <laurens@mistral.ai>
This commit is contained in:
Mathias Gesbert 2026-03-12 15:57:44 +01:00 committed by GitHub
parent e9428bce23
commit 9421fbc08e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 370 additions and 40 deletions

View file

@ -3,4 +3,4 @@ from __future__ import annotations
from pathlib import Path
VIBE_ROOT = Path(__file__).parent
__version__ = "2.4.1"
__version__ = "2.4.2"

View file

@ -537,7 +537,11 @@ class VibeApp(App): # noqa: PLR0904
if not self.agent_loop:
return False
skill_name = user_input[1:].strip().lower()
parts = user_input[1:].strip().split(None, 1)
if not parts:
return False
skill_name = parts[0].lower()
skill_info = self.agent_loop.skill_manager.get_skill(skill_name)
if not skill_info:
return False
@ -554,6 +558,9 @@ class VibeApp(App): # noqa: PLR0904
)
return True
if len(parts) > 1:
skill_content = f"{user_input}\n\n{skill_content}"
await self._handle_user_message(skill_content)
return True
@ -1599,7 +1606,7 @@ class VibeApp(App): # noqa: PLR0904
f"{update_message_prefix}\nVibe was updated successfully. Please restart to use the new version.",
title="Update successful",
severity="information",
timeout=10,
timeout=float("inf"),
)
return

View file

@ -199,7 +199,9 @@ class AgentLoop:
self.session_id = str(uuid4())
self._current_user_message_id: str | None = None
self.telemetry_client = TelemetryClient(config_getter=lambda: self.config)
self.telemetry_client = TelemetryClient(
config_getter=lambda: self.config, session_id_getter=lambda: self.session_id
)
self.session_logger = SessionLogger(config.session_logging, self.session_id)
self._teleport_service: TeleportService | None = None
@ -297,6 +299,7 @@ class AgentLoop:
nuage_base_url=self.config.nuage_base_url,
nuage_workflow_id=self.config.nuage_workflow_id,
nuage_api_key=self.config.nuage_api_key,
nuage_task_queue=self.config.nuage_task_queue,
)
return self._teleport_service

View file

@ -309,11 +309,13 @@ class VibeConfig(BaseSettings):
enable_auto_update: bool = True
enable_notifications: bool = True
api_timeout: float = 720.0
auto_compact_threshold: int = 200_000
# TODO(vibe-nuage): remove exclude=True once the feature is publicly available
nuage_enabled: bool = Field(default=False, exclude=True)
nuage_base_url: str = Field(default="https://api.globalaegis.net", exclude=True)
nuage_workflow_id: str = Field(default="__shared-nuage-workflow", exclude=True)
nuage_task_queue: str | None = Field(default="shared-vibe-nuage", exclude=True)
# TODO(vibe-nuage): change default value to MISTRAL_API_KEY once prod has shared vibe-nuage workers
nuage_api_key_env_var: str = Field(default="STAGING_MISTRAL_API_KEY", exclude=True)
@ -466,6 +468,18 @@ class VibeConfig(BaseSettings):
file_secret_settings,
)
@model_validator(mode="after")
def _apply_global_auto_compact_threshold(self) -> VibeConfig:
self.models = [
model
if "auto_compact_threshold" in model.model_fields_set
else model.model_copy(
update={"auto_compact_threshold": self.auto_compact_threshold}
)
for model in self.models
]
return self
@model_validator(mode="after")
def _check_api_key(self) -> VibeConfig:
try:

View file

@ -19,8 +19,13 @@ DATALAKE_EVENTS_URL = "https://codestral.mistral.ai/v1/datalake/events"
class TelemetryClient:
def __init__(self, config_getter: Callable[[], VibeConfig]) -> None:
def __init__(
self,
config_getter: Callable[[], VibeConfig],
session_id_getter: Callable[[], str | None] | None = None,
) -> None:
self._config_getter = config_getter
self._session_id_getter = session_id_getter
self._client: httpx.AsyncClient | None = None
self._pending_tasks: set[asyncio.Task[Any]] = set()
@ -71,6 +76,11 @@ class TelemetryClient:
if mistral_api_key is None or not self._is_enabled():
return
user_agent = self._get_telemetry_user_agent()
if (
self._session_id_getter is not None
and (session_id := self._session_id_getter()) is not None
):
properties = {**properties, "session_id": session_id}
async def _send() -> None:
try:

View file

@ -71,12 +71,14 @@ class NuageClient:
api_key: str,
workflow_id: str,
*,
task_queue: str | None = None,
client: httpx.AsyncClient | None = None,
timeout: float = 60.0,
) -> None:
self._base_url = base_url.rstrip("/")
self._api_key = api_key
self._workflow_id = workflow_id
self._task_queue = task_queue
self._client = client
self._owns_client = client is None
self._timeout = timeout
@ -113,7 +115,10 @@ class NuageClient:
response = await self._http_client.post(
f"{self._base_url}/v1/workflows/{self._workflow_id}/execute",
headers=self._headers(),
json={"input": params.model_dump(mode="json")},
json={
"input": params.model_dump(mode="json"),
"task_queue": self._task_queue,
},
)
if not response.is_success:
error_msg = f"Nuage workflow trigger failed: {response.text}"

View file

@ -48,6 +48,7 @@ class TeleportService:
nuage_api_key: str,
workdir: Path | None = None,
*,
nuage_task_queue: str | None = None,
client: httpx.AsyncClient | None = None,
timeout: float = 60.0,
) -> None:
@ -55,6 +56,7 @@ class TeleportService:
self._nuage_base_url = nuage_base_url
self._nuage_workflow_id = nuage_workflow_id
self._nuage_api_key = nuage_api_key
self._nuage_task_queue = nuage_task_queue
self._git = GitRepository(workdir)
self._client = client
self._owns_client = client is None
@ -70,6 +72,7 @@ class TeleportService:
self._nuage_base_url,
self._nuage_api_key,
self._nuage_workflow_id,
task_queue=self._nuage_task_queue,
client=self._client,
)
await self._git.__aenter__()
@ -106,6 +109,7 @@ class TeleportService:
self._nuage_base_url,
self._nuage_api_key,
self._nuage_workflow_id,
task_queue=self._nuage_task_queue,
client=self._http_client,
)
return self._nuage

View file

@ -62,7 +62,6 @@ class SearchReplaceResult(BaseModel):
lines_changed: int
content: str
warnings: list[str] = Field(default_factory=list)
file_content_before: str
class SearchReplaceConfig(BaseToolConfig):
@ -163,7 +162,6 @@ class SearchReplace(
lines_changed=lines_changed,
warnings=block_result.warnings,
content=args.content,
file_content_before=original_content,
)
@final

View file

@ -33,7 +33,6 @@ class WriteFileResult(BaseModel):
bytes_written: int
file_existed: bool
content: str
file_content_before: str | None = None
class WriteFileConfig(BaseToolConfig):
@ -85,14 +84,6 @@ class WriteFile(
) -> AsyncGenerator[ToolStreamEvent | WriteFileResult, None]:
file_path, file_existed, content_bytes = self._prepare_and_validate_path(args)
file_content_before: str | None = None
if file_existed and args.overwrite:
try:
async with await anyio.Path(file_path).open(encoding="utf-8") as f:
file_content_before = await f.read(524_288) # 512kb
except Exception:
pass
await self._write_file(args, file_path)
yield WriteFileResult(
@ -100,7 +91,6 @@ class WriteFile(
bytes_written=content_bytes,
file_existed=file_existed,
content=args.content,
file_content_before=file_content_before,
)
def _prepare_and_validate_path(self, args: WriteFileArgs) -> tuple[Path, bool, int]:

View file

@ -1,3 +1,4 @@
# What's new in v2.4.1
- **Prompt mode**: Disabled interactive questions in prompt mode for smoother non-interactive usage
- **VS Code terminal fix**: Space key now works correctly in all input widgets (question prompts, proxy setup)
# What's new in v2.4.2
- **Skill arguments**: Skills now extract arguments when invoked, allowing you to pass arguments
- **Auto-compact fallback**: Auto-compact threshold falls back to the global setting when not defined at model level