v2.18.0 (#843)
Co-authored-by: Clément Drouin <clement.drouin@mistral.ai> Co-authored-by: Clément Sirieix <clement.sirieix@mistral.ai> Co-authored-by: Cyprien <courtot.c@gmail.com> Co-authored-by: Guillaume LE GOFF <guillaume.lgf@gmail.com> Co-authored-by: Jean Burellier <sheplu@users.noreply.github.com> Co-authored-by: Kim-Adeline Miguel <51720070+kimadeline@users.noreply.github.com> Co-authored-by: Mathias Gesbert <mathias.gesbert@mistral.ai> Co-authored-by: Nelson PROIA <144663685+Nelson-PROIA@users.noreply.github.com> Co-authored-by: Paul VEZIA <166131032+le-codeur-rapide@users.noreply.github.com> Co-authored-by: Pierre Rossinès <pierre.rossines@mistral.ai> Co-authored-by: Quentin <quentin.torroba@mistral.ai> Co-authored-by: Vincent G <10739306+VinceOPS@users.noreply.github.com> Co-authored-by: josephine-delas <57808586+josephine-delas@users.noreply.github.com> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
parent
725d3a56ce
commit
e607ccbb00
242 changed files with 7372 additions and 1974 deletions
|
|
@ -99,6 +99,7 @@ async def test_acp_initialize(binary: Path) -> None:
|
|||
env = os.environ.copy()
|
||||
env["VIBE_HOME"] = str(vibe_home)
|
||||
env["MISTRAL_API_KEY"] = "smoke-test-mock-key"
|
||||
env["VIBE_TEST_DISABLE_KEYRING"] = "1"
|
||||
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
str(binary),
|
||||
|
|
|
|||
|
|
@ -206,6 +206,7 @@ async def get_acp_agent_loop_process(
|
|||
env.update(mock_env)
|
||||
env["MISTRAL_API_KEY"] = "mock"
|
||||
env["VIBE_HOME"] = str(vibe_home)
|
||||
env["VIBE_TEST_DISABLE_KEYRING"] = "1"
|
||||
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
*cmd,
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ def _build_env(vibe_home_dir: Path, *, include_api_key: bool) -> dict[str, str]:
|
|||
env = os.environ.copy()
|
||||
env["PYTHONUNBUFFERED"] = "1"
|
||||
env["VIBE_HOME"] = str(vibe_home_dir)
|
||||
env["VIBE_TEST_DISABLE_KEYRING"] = "1"
|
||||
|
||||
vibe_home_dir.mkdir(parents=True, exist_ok=True)
|
||||
config_file = vibe_home_dir / "config.toml"
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ class TestACPAuthSignOut:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_removes_keyring_key(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
deleted: list[str] = []
|
||||
deleted: list[tuple[str, str]] = []
|
||||
monkeypatch.delenv(DEFAULT_MISTRAL_API_ENV_KEY, raising=False)
|
||||
monkeypatch.setattr(
|
||||
keyring, "get_password", lambda service, username: "keyring-key"
|
||||
|
|
@ -268,14 +268,17 @@ class TestACPAuthSignOut:
|
|||
monkeypatch.setattr(
|
||||
keyring,
|
||||
"delete_password",
|
||||
lambda service, username: deleted.append(username),
|
||||
lambda service, username: deleted.append((service, username)),
|
||||
)
|
||||
acp_agent_loop = build_acp_agent_loop(build_mistral_provider())
|
||||
|
||||
response = await acp_agent_loop.ext_method("auth/signOut", {})
|
||||
|
||||
assert response == {}
|
||||
assert deleted == [DEFAULT_MISTRAL_API_ENV_KEY]
|
||||
assert deleted == [
|
||||
("ai.mistral.vibe", DEFAULT_MISTRAL_API_ENV_KEY),
|
||||
("vibe", DEFAULT_MISTRAL_API_ENV_KEY),
|
||||
]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_surfaces_internal_error_when_keyring_delete_fails(
|
||||
|
|
|
|||
|
|
@ -290,6 +290,37 @@ class TestAcpBashTimeout:
|
|||
assert str(exc_info.value) == "Command timed out after 1s: 'slow_command'"
|
||||
assert custom_handle._killed
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_timeout_bounded_when_kill_hangs(
|
||||
self, mock_client: MockClient, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.setattr(
|
||||
"vibe.acp.tools.builtins.bash._TERMINAL_CLEANUP_TIMEOUT", 0.05
|
||||
)
|
||||
|
||||
custom_handle = MockTerminalHandle(
|
||||
terminal_id="hanging_kill_terminal", wait_delay=20
|
||||
)
|
||||
mock_client._terminal_handle = custom_handle
|
||||
|
||||
async def hanging_kill() -> None:
|
||||
await asyncio.sleep(30)
|
||||
|
||||
custom_handle.kill = hanging_kill
|
||||
|
||||
tool = Bash(
|
||||
config_getter=lambda: BashToolConfig(),
|
||||
state=AcpBashState.model_construct(
|
||||
client=mock_client, session_id="test_session"
|
||||
),
|
||||
)
|
||||
|
||||
args = BashArgs(command="slow_command", timeout=1)
|
||||
with pytest.raises(ToolError) as exc_info:
|
||||
await asyncio.wait_for(collect_result(tool.run(args)), timeout=5)
|
||||
|
||||
assert str(exc_info.value) == "Command timed out after 1s: 'slow_command'"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_timeout_handles_kill_failure(
|
||||
self, mock_client: MockClient
|
||||
|
|
|
|||
|
|
@ -156,6 +156,34 @@ class TestACPForkSession:
|
|||
forked_session = acp_agent_loop.sessions[response.session_id]
|
||||
assert forked_session.agent_loop.parent_session_id == source_session.id
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fork_session_inherits_limits(
|
||||
self, acp_agent_loop: VibeAcpAgentLoop
|
||||
) -> None:
|
||||
session_response = await acp_agent_loop.new_session(
|
||||
cwd=str(Path.cwd()), mcp_servers=[]
|
||||
)
|
||||
source_session = acp_agent_loop.sessions[session_response.session_id]
|
||||
|
||||
await acp_agent_loop.set_config_option(
|
||||
session_id=source_session.id, config_id="max_turns", value="42"
|
||||
)
|
||||
await acp_agent_loop.set_config_option(
|
||||
session_id=source_session.id, config_id="max_tokens", value="8192"
|
||||
)
|
||||
source_session.agent_loop._max_price = 1.5
|
||||
source_session.agent_loop._max_session_tokens = 100_000
|
||||
|
||||
response = await acp_agent_loop.fork_session(
|
||||
cwd=str(Path.cwd()), session_id=source_session.id, mcp_servers=[]
|
||||
)
|
||||
|
||||
forked = acp_agent_loop.sessions[response.session_id].agent_loop
|
||||
assert forked._max_turns == 42
|
||||
assert forked._max_tokens == 8192
|
||||
assert forked._max_price == 1.5
|
||||
assert forked._max_session_tokens == 100_000
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fork_session_rejects_running_session(
|
||||
self, acp_agent_loop: VibeAcpAgentLoop
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ class TestACPInitialize:
|
|||
),
|
||||
)
|
||||
assert response.agent_info == Implementation(
|
||||
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="2.17.1"
|
||||
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="2.18.0"
|
||||
)
|
||||
|
||||
assert response.auth_methods is not None
|
||||
|
|
@ -172,7 +172,7 @@ class TestACPInitialize:
|
|||
),
|
||||
)
|
||||
assert response.agent_info == Implementation(
|
||||
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="2.17.1"
|
||||
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="2.18.0"
|
||||
)
|
||||
|
||||
assert response.auth_methods is not None
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ def acp_agent_with_session_config(
|
|||
),
|
||||
],
|
||||
session_logging=session_config,
|
||||
include_project_context=True,
|
||||
)
|
||||
|
||||
class PatchedAgentLoop(AgentLoop):
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ def _workspace_trust_meta(session_response: NewSessionResponse) -> dict:
|
|||
def acp_agent_loop(backend) -> VibeAcpAgentLoop:
|
||||
config = build_test_vibe_config(
|
||||
active_model="devstral-latest",
|
||||
include_project_context=True,
|
||||
models=[
|
||||
ModelConfig(
|
||||
name="devstral-latest", provider="mistral", alias="devstral-latest"
|
||||
|
|
|
|||
|
|
@ -528,3 +528,67 @@ class TestACPSetConfigOptionMaxTurns:
|
|||
]
|
||||
assert len(turn_limits) == 1
|
||||
assert turn_limits[0].max_turns == 200
|
||||
|
||||
|
||||
class TestACPSetConfigOptionMaxTokens:
|
||||
@pytest.mark.asyncio
|
||||
async def test_set_config_option_max_tokens_success(
|
||||
self, acp_agent_loop: VibeAcpAgentLoop
|
||||
) -> None:
|
||||
session_response = await acp_agent_loop.new_session(
|
||||
cwd=str(Path.cwd()), mcp_servers=[]
|
||||
)
|
||||
session_id = session_response.session_id
|
||||
acp_session = next(
|
||||
(s for s in acp_agent_loop.sessions.values() if s.id == session_id), None
|
||||
)
|
||||
assert acp_session is not None
|
||||
|
||||
response = await acp_agent_loop.set_config_option(
|
||||
session_id=session_id, config_id="max_tokens", value="8192"
|
||||
)
|
||||
|
||||
assert response is not None
|
||||
assert acp_session.agent_loop._max_tokens == 8192
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_set_config_option_max_tokens_invalid_string_returns_none(
|
||||
self, acp_agent_loop: VibeAcpAgentLoop
|
||||
) -> None:
|
||||
session_response = await acp_agent_loop.new_session(
|
||||
cwd=str(Path.cwd()), mcp_servers=[]
|
||||
)
|
||||
session_id = session_response.session_id
|
||||
acp_session = next(
|
||||
(s for s in acp_agent_loop.sessions.values() if s.id == session_id), None
|
||||
)
|
||||
assert acp_session is not None
|
||||
initial_max_tokens = acp_session.agent_loop._max_tokens
|
||||
|
||||
response = await acp_agent_loop.set_config_option(
|
||||
session_id=session_id, config_id="max_tokens", value="abc"
|
||||
)
|
||||
|
||||
assert response is None
|
||||
assert acp_session.agent_loop._max_tokens == initial_max_tokens
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_set_config_option_max_tokens_bool_returns_none(
|
||||
self, acp_agent_loop: VibeAcpAgentLoop
|
||||
) -> None:
|
||||
session_response = await acp_agent_loop.new_session(
|
||||
cwd=str(Path.cwd()), mcp_servers=[]
|
||||
)
|
||||
session_id = session_response.session_id
|
||||
acp_session = next(
|
||||
(s for s in acp_agent_loop.sessions.values() if s.id == session_id), None
|
||||
)
|
||||
assert acp_session is not None
|
||||
initial_max_tokens = acp_session.agent_loop._max_tokens
|
||||
|
||||
response = await acp_agent_loop.set_config_option(
|
||||
session_id=session_id, config_id="max_tokens", value=True
|
||||
)
|
||||
|
||||
assert response is None
|
||||
assert acp_session.agent_loop._max_tokens == initial_max_tokens
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue