v2.7.4 (#579)
Co-authored-by: Clément Sirieix <clement.sirieix@mistral.ai> Co-authored-by: Kim-Adeline Miguel <kimadeline.miguel@mistral.ai> Co-authored-by: Lucas Marandat <31749711+lucasmrdt@users.noreply.github.com> Co-authored-by: Michel Thomazo <51709227+michelTho@users.noreply.github.com> Co-authored-by: Paul Cacheux <paul.cacheux@mistral.ai> Co-authored-by: Peter Evers <pevers90@gmail.com> Co-authored-by: Pierre Rossinès <pierre.rossines@mistral.ai> Co-authored-by: Pierre Rossinès <pierre.rossines@protonmail.com> Co-authored-by: Quentin <quentin.torroba@mistral.ai> Co-authored-by: Simon Van de Kerckhove <simon.vandekerckhove@mistral.ai> Co-authored-by: Val <102326092+vdeva@users.noreply.github.com> Co-authored-by: Vincent G <10739306+VinceOPS@users.noreply.github.com> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
parent
90763daf81
commit
e9a9217cc8
113 changed files with 7202 additions and 541 deletions
|
|
@ -239,6 +239,32 @@ def test_vibe_acp_setup_shows_onboarding_and_exits_on_cancel(
|
|||
assert "Setup cancelled" in output
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_vibe_acp_survives_broken_config(vibe_home_dir: Path) -> None:
|
||||
vibe_home_dir.mkdir(parents=True, exist_ok=True)
|
||||
(vibe_home_dir / "config.toml").write_text("{{{{invalid toml content!!")
|
||||
|
||||
proc, _initialize_response, conn = await _connect_and_initialize(
|
||||
vibe_home_dir=vibe_home_dir, include_api_key=True
|
||||
)
|
||||
|
||||
try:
|
||||
# new_session should return a structured JSON-RPC error, not crash the server
|
||||
with pytest.raises(RequestError):
|
||||
await asyncio.wait_for(
|
||||
conn.new_session(cwd=str(Path.cwd()), mcp_servers=[]), timeout=10
|
||||
)
|
||||
assert proc.returncode is None, "Server crashed after broken config"
|
||||
|
||||
(vibe_home_dir / "config.toml").write_text("")
|
||||
session = await asyncio.wait_for(
|
||||
conn.new_session(cwd=str(Path.cwd()), mcp_servers=[]), timeout=10
|
||||
)
|
||||
assert session.session_id
|
||||
finally:
|
||||
await _terminate_process(proc)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_vibe_acp_new_session_fails_without_api_key(vibe_home_dir: Path) -> None:
|
||||
proc, _initialize_response, conn = await _connect_and_initialize(
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ def acp_bash_tool(mock_client: MockClient) -> Bash:
|
|||
session_id="test_session_123",
|
||||
tool_call_id="test_tool_call_456",
|
||||
)
|
||||
return Bash(config=config, state=state)
|
||||
return Bash(config_getter=lambda: config, state=state)
|
||||
|
||||
|
||||
class TestAcpBashBasic:
|
||||
|
|
@ -154,7 +154,7 @@ class TestAcpBashExecution:
|
|||
self, mock_client: MockClient
|
||||
) -> None:
|
||||
tool = Bash(
|
||||
config=BashToolConfig(),
|
||||
config_getter=lambda: BashToolConfig(),
|
||||
state=AcpBashState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -174,7 +174,7 @@ class TestAcpBashExecution:
|
|||
mock_client._terminal_handle = custom_handle
|
||||
|
||||
tool = Bash(
|
||||
config=BashToolConfig(),
|
||||
config_getter=lambda: BashToolConfig(),
|
||||
state=AcpBashState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -194,7 +194,7 @@ class TestAcpBashExecution:
|
|||
mock_client._create_terminal_error = RuntimeError("Connection failed")
|
||||
|
||||
tool = Bash(
|
||||
config=BashToolConfig(),
|
||||
config_getter=lambda: BashToolConfig(),
|
||||
state=AcpBashState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -212,7 +212,7 @@ class TestAcpBashExecution:
|
|||
@pytest.mark.asyncio
|
||||
async def test_run_without_client(self) -> None:
|
||||
tool = Bash(
|
||||
config=BashToolConfig(),
|
||||
config_getter=lambda: BashToolConfig(),
|
||||
state=AcpBashState.model_construct(
|
||||
client=None, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -231,7 +231,7 @@ class TestAcpBashExecution:
|
|||
async def test_run_without_session_id(self) -> None:
|
||||
mock_client = MockClient()
|
||||
tool = Bash(
|
||||
config=BashToolConfig(),
|
||||
config_getter=lambda: BashToolConfig(),
|
||||
state=AcpBashState.model_construct(
|
||||
client=mock_client, session_id=None, tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -254,7 +254,7 @@ class TestAcpBashExecution:
|
|||
mock_client._terminal_handle = custom_handle
|
||||
|
||||
tool = Bash(
|
||||
config=BashToolConfig(),
|
||||
config_getter=lambda: BashToolConfig(),
|
||||
state=AcpBashState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -281,7 +281,7 @@ class TestAcpBashTimeout:
|
|||
|
||||
# Use a config with different default timeout to verify args timeout overrides it
|
||||
tool = Bash(
|
||||
config=BashToolConfig(default_timeout=30),
|
||||
config_getter=lambda: BashToolConfig(default_timeout=30),
|
||||
state=AcpBashState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -310,7 +310,7 @@ class TestAcpBashTimeout:
|
|||
custom_handle.kill = failing_kill
|
||||
|
||||
tool = Bash(
|
||||
config=BashToolConfig(),
|
||||
config_getter=lambda: BashToolConfig(),
|
||||
state=AcpBashState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -328,7 +328,7 @@ class TestAcpBashEmbedding:
|
|||
@pytest.mark.asyncio
|
||||
async def test_run_with_embedding(self, mock_client: MockClient) -> None:
|
||||
tool = Bash(
|
||||
config=BashToolConfig(),
|
||||
config_getter=lambda: BashToolConfig(),
|
||||
state=AcpBashState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -344,7 +344,7 @@ class TestAcpBashEmbedding:
|
|||
self, mock_client: MockClient
|
||||
) -> None:
|
||||
tool = Bash(
|
||||
config=BashToolConfig(),
|
||||
config_getter=lambda: BashToolConfig(),
|
||||
state=AcpBashState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id=None
|
||||
),
|
||||
|
|
@ -367,7 +367,7 @@ class TestAcpBashEmbedding:
|
|||
mock_client.session_update = failing_session_update
|
||||
|
||||
tool = Bash(
|
||||
config=BashToolConfig(),
|
||||
config_getter=lambda: BashToolConfig(),
|
||||
state=AcpBashState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -393,7 +393,7 @@ class TestAcpBashConfig:
|
|||
mock_client._terminal_handle = custom_handle
|
||||
|
||||
tool = Bash(
|
||||
config=BashToolConfig(default_timeout=30),
|
||||
config_getter=lambda: BashToolConfig(default_timeout=30),
|
||||
state=AcpBashState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -423,7 +423,7 @@ class TestAcpBashCleanup:
|
|||
custom_handle.release = mock_release
|
||||
|
||||
tool = Bash(
|
||||
config=BashToolConfig(),
|
||||
config_getter=lambda: BashToolConfig(),
|
||||
state=AcpBashState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -455,7 +455,7 @@ class TestAcpBashCleanup:
|
|||
custom_handle.release = mock_release
|
||||
|
||||
tool = Bash(
|
||||
config=BashToolConfig(),
|
||||
config_getter=lambda: BashToolConfig(),
|
||||
state=AcpBashState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -481,7 +481,7 @@ class TestAcpBashCleanup:
|
|||
mock_client._terminal_handle = custom_handle
|
||||
|
||||
tool = Bash(
|
||||
config=BashToolConfig(),
|
||||
config_getter=lambda: BashToolConfig(),
|
||||
state=AcpBashState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class TestACPInitialize:
|
|||
session_capabilities=SessionCapabilities(list=SessionListCapabilities()),
|
||||
)
|
||||
assert response.agent_info == Implementation(
|
||||
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="2.7.3"
|
||||
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="2.7.4"
|
||||
)
|
||||
|
||||
assert response.auth_methods == []
|
||||
|
|
@ -52,7 +52,7 @@ class TestACPInitialize:
|
|||
session_capabilities=SessionCapabilities(list=SessionListCapabilities()),
|
||||
)
|
||||
assert response.agent_info == Implementation(
|
||||
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="2.7.3"
|
||||
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="2.7.4"
|
||||
)
|
||||
|
||||
assert response.auth_methods is not None
|
||||
|
|
|
|||
|
|
@ -291,6 +291,42 @@ class TestLoadSession:
|
|||
assert len(thought_updates) == 1
|
||||
assert thought_updates[0].update.content.text == "Let me think step by step..."
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_load_session_replays_reasoning_before_assistant_message(
|
||||
self,
|
||||
acp_agent_with_session_config: tuple[VibeAcpAgentLoop, FakeClient],
|
||||
temp_session_dir: Path,
|
||||
create_test_session,
|
||||
) -> None:
|
||||
acp_agent, client = acp_agent_with_session_config
|
||||
|
||||
session_id = "replay-order-1234"
|
||||
cwd = str(Path.cwd())
|
||||
messages = [
|
||||
{"role": "user", "content": "Think about this"},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Here is my answer",
|
||||
"reasoning_content": "Let me think step by step...",
|
||||
},
|
||||
]
|
||||
create_test_session(temp_session_dir, session_id, cwd, messages=messages)
|
||||
|
||||
await acp_agent.load_session(cwd=cwd, mcp_servers=[], session_id=session_id)
|
||||
|
||||
response_updates = [
|
||||
update.update
|
||||
for update in client._session_updates
|
||||
if isinstance(update.update, (AgentThoughtChunk, AgentMessageChunk))
|
||||
]
|
||||
|
||||
assert [type(update) for update in response_updates] == [
|
||||
AgentThoughtChunk,
|
||||
AgentMessageChunk,
|
||||
]
|
||||
assert response_updates[0].content.text == "Let me think step by step..."
|
||||
assert response_updates[1].content.text == "Here is my answer"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_load_session_not_found_raises_error(
|
||||
self, acp_agent_with_session_config: tuple[VibeAcpAgentLoop, FakeClient]
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ def acp_read_file_tool(
|
|||
session_id="test_session_123",
|
||||
tool_call_id="test_tool_call_456",
|
||||
)
|
||||
return ReadFile(config=config, state=state)
|
||||
return ReadFile(config_getter=lambda: config, state=state)
|
||||
|
||||
|
||||
class TestAcpReadFileBasic:
|
||||
|
|
@ -116,7 +116,7 @@ class TestAcpReadFileExecution:
|
|||
test_file = tmp_path / "test_file.txt"
|
||||
test_file.touch()
|
||||
tool = ReadFile(
|
||||
config=ReadFileToolConfig(),
|
||||
config_getter=lambda: ReadFileToolConfig(),
|
||||
state=AcpReadFileState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -139,7 +139,7 @@ class TestAcpReadFileExecution:
|
|||
test_file = tmp_path / "test_file.txt"
|
||||
test_file.touch()
|
||||
tool = ReadFile(
|
||||
config=ReadFileToolConfig(),
|
||||
config_getter=lambda: ReadFileToolConfig(),
|
||||
state=AcpReadFileState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -162,7 +162,7 @@ class TestAcpReadFileExecution:
|
|||
test_file = tmp_path / "test_file.txt"
|
||||
test_file.touch()
|
||||
tool = ReadFile(
|
||||
config=ReadFileToolConfig(),
|
||||
config_getter=lambda: ReadFileToolConfig(),
|
||||
state=AcpReadFileState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -187,7 +187,7 @@ class TestAcpReadFileExecution:
|
|||
test_file = tmp_path / "test.txt"
|
||||
test_file.touch()
|
||||
tool = ReadFile(
|
||||
config=ReadFileToolConfig(),
|
||||
config_getter=lambda: ReadFileToolConfig(),
|
||||
state=AcpReadFileState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -207,7 +207,7 @@ class TestAcpReadFileExecution:
|
|||
test_file = tmp_path / "test.txt"
|
||||
test_file.touch()
|
||||
tool = ReadFile(
|
||||
config=ReadFileToolConfig(),
|
||||
config_getter=lambda: ReadFileToolConfig(),
|
||||
state=AcpReadFileState.model_construct(
|
||||
client=None, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -231,7 +231,7 @@ class TestAcpReadFileExecution:
|
|||
test_file.touch()
|
||||
mock_client = MockClient()
|
||||
tool = ReadFile(
|
||||
config=ReadFileToolConfig(),
|
||||
config_getter=lambda: ReadFileToolConfig(),
|
||||
state=AcpReadFileState.model_construct(
|
||||
client=mock_client, session_id=None, tool_call_id="test_call"
|
||||
),
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ def acp_search_replace_tool(
|
|||
session_id="test_session_123",
|
||||
tool_call_id="test_tool_call_456",
|
||||
)
|
||||
return SearchReplace(config=config, state=state)
|
||||
return SearchReplace(config_getter=lambda: config, state=state)
|
||||
|
||||
|
||||
class TestAcpSearchReplaceBasic:
|
||||
|
|
@ -139,7 +139,7 @@ class TestAcpSearchReplaceExecution:
|
|||
monkeypatch.chdir(tmp_path)
|
||||
config = SearchReplaceConfig(create_backup=True)
|
||||
tool = SearchReplace(
|
||||
config=config,
|
||||
config_getter=lambda: config,
|
||||
state=AcpSearchReplaceState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -169,7 +169,7 @@ class TestAcpSearchReplaceExecution:
|
|||
mock_client._read_error = RuntimeError("File not found")
|
||||
|
||||
tool = SearchReplace(
|
||||
config=SearchReplaceConfig(),
|
||||
config_getter=lambda: SearchReplaceConfig(),
|
||||
state=AcpSearchReplaceState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -200,7 +200,7 @@ class TestAcpSearchReplaceExecution:
|
|||
mock_client._file_content = "old" # Update mock to return correct content
|
||||
|
||||
tool = SearchReplace(
|
||||
config=SearchReplaceConfig(),
|
||||
config_getter=lambda: SearchReplaceConfig(),
|
||||
state=AcpSearchReplaceState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -243,7 +243,7 @@ class TestAcpSearchReplaceExecution:
|
|||
test_file = tmp_path / "test.txt"
|
||||
test_file.touch()
|
||||
tool = SearchReplace(
|
||||
config=SearchReplaceConfig(),
|
||||
config_getter=lambda: SearchReplaceConfig(),
|
||||
state=AcpSearchReplaceState.model_construct(
|
||||
client=client, session_id=session_id, tool_call_id="test_call"
|
||||
),
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ def acp_write_file_tool(
|
|||
session_id="test_session_123",
|
||||
tool_call_id="test_tool_call_456",
|
||||
)
|
||||
return WriteFile(config=config, state=state)
|
||||
return WriteFile(config_getter=lambda: config, state=state)
|
||||
|
||||
|
||||
class TestAcpWriteFileBasic:
|
||||
|
|
@ -95,7 +95,7 @@ class TestAcpWriteFileExecution:
|
|||
) -> None:
|
||||
monkeypatch.chdir(tmp_path)
|
||||
tool = WriteFile(
|
||||
config=WriteFileConfig(),
|
||||
config_getter=lambda: WriteFileConfig(),
|
||||
state=AcpWriteFileState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -130,7 +130,7 @@ class TestAcpWriteFileExecution:
|
|||
mock_client._write_error = RuntimeError("Permission denied")
|
||||
|
||||
tool = WriteFile(
|
||||
config=WriteFileConfig(),
|
||||
config_getter=lambda: WriteFileConfig(),
|
||||
state=AcpWriteFileState.model_construct(
|
||||
client=mock_client, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -149,7 +149,7 @@ class TestAcpWriteFileExecution:
|
|||
) -> None:
|
||||
monkeypatch.chdir(tmp_path)
|
||||
tool = WriteFile(
|
||||
config=WriteFileConfig(),
|
||||
config_getter=lambda: WriteFileConfig(),
|
||||
state=AcpWriteFileState.model_construct(
|
||||
client=None, session_id="test_session", tool_call_id="test_call"
|
||||
),
|
||||
|
|
@ -171,7 +171,7 @@ class TestAcpWriteFileExecution:
|
|||
monkeypatch.chdir(tmp_path)
|
||||
mock_client = MockClient()
|
||||
tool = WriteFile(
|
||||
config=WriteFileConfig(),
|
||||
config_getter=lambda: WriteFileConfig(),
|
||||
state=AcpWriteFileState.model_construct(
|
||||
client=mock_client, session_id=None, tool_call_id="test_call"
|
||||
),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue