Co-authored-by: Clément Sirieix <clement.sirieix@mistral.ai>
Co-authored-by: Jean-Malo Delignon <56539593+jean-malo@users.noreply.github.com>
Co-authored-by: Paul Cacheux <paul.cacheux@mistral.ai>
Co-authored-by: Quentin <torroba.q@gmail.com>
Co-authored-by: angelapopopo <angele.lenglemetz@mistral.ai>
Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
Clément Drouin 2026-03-31 16:28:55 +02:00 committed by GitHub
parent 6a50d1d521
commit 54b9a17457
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 1000 additions and 200 deletions

View file

@ -0,0 +1,156 @@
from __future__ import annotations
from pathlib import Path
from unittest.mock import patch
from uuid import UUID
from acp.schema import AgentMessageChunk, TextContentBlock
import pytest
from tests.acp.conftest import _create_acp_agent
from tests.stubs.fake_backend import FakeBackend
from tests.stubs.fake_client import FakeClient
from vibe.acp.acp_agent_loop import VibeAcpAgentLoop
from vibe.core.agent_loop import AgentLoop
from vibe.core.types import LLMChunk, LLMMessage, LLMUsage, Role
def _is_uuid(value: str) -> bool:
try:
UUID(value)
return True
except ValueError:
return False
def _make_response_chunk(content: str = "Hi") -> LLMChunk:
return LLMChunk(
message=LLMMessage(role=Role.assistant, content=content),
usage=LLMUsage(prompt_tokens=1, completion_tokens=1),
)
@pytest.fixture
def two_turn_acp_agent_loop() -> VibeAcpAgentLoop:
backend = FakeBackend([
[_make_response_chunk("Hi")],
[_make_response_chunk("Hi again")],
])
class PatchedAgentLoop(AgentLoop):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **{**kwargs, "backend": backend})
patch("vibe.acp.acp_agent_loop.AgentLoop", side_effect=PatchedAgentLoop).start()
return _create_acp_agent()
class TestPromptResponseUserMessageId:
@pytest.mark.asyncio
async def test_generates_user_message_id_when_client_provides_none(
self, acp_agent_loop: VibeAcpAgentLoop
) -> None:
session_response = await acp_agent_loop.new_session(
cwd=str(Path.cwd()), mcp_servers=[]
)
response = await acp_agent_loop.prompt(
session_id=session_response.session_id,
prompt=[TextContentBlock(type="text", text="hi")],
)
assert response.user_message_id is not None
assert _is_uuid(response.user_message_id)
@pytest.mark.asyncio
async def test_echoes_client_provided_message_id(
self, acp_agent_loop: VibeAcpAgentLoop
) -> None:
session_response = await acp_agent_loop.new_session(
cwd=str(Path.cwd()), mcp_servers=[]
)
client_message_id = "550e8400-e29b-41d4-a716-446655440000"
response = await acp_agent_loop.prompt(
session_id=session_response.session_id,
prompt=[TextContentBlock(type="text", text="hi")],
message_id=client_message_id,
)
assert response.user_message_id == client_message_id
@pytest.mark.asyncio
async def test_user_message_ids_are_unique_across_turns(
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
response_1 = await acp_agent_loop.prompt(
session_id=session_id, prompt=[TextContentBlock(type="text", text="hi")]
)
response_2 = await acp_agent_loop.prompt(
session_id=session_id,
prompt=[TextContentBlock(type="text", text="hi again")],
)
assert response_1.user_message_id != response_2.user_message_id
class TestAgentMessageChunkMessageId:
@pytest.mark.asyncio
async def test_agent_message_chunk_has_message_id(
self, acp_agent_loop: VibeAcpAgentLoop
) -> None:
session_response = await acp_agent_loop.new_session(
cwd=str(Path.cwd()), mcp_servers=[]
)
await acp_agent_loop.prompt(
session_id=session_response.session_id,
prompt=[TextContentBlock(type="text", text="hi")],
)
fake_client: FakeClient = acp_agent_loop.client # type: ignore
agent_chunks = [
u
for u in fake_client._session_updates
if isinstance(u.update, AgentMessageChunk)
]
assert len(agent_chunks) >= 1
assert agent_chunks[0].update.message_id is not None
@pytest.mark.asyncio
async def test_agent_message_ids_are_unique_across_turns(
self, two_turn_acp_agent_loop: VibeAcpAgentLoop
) -> None:
session_response = await two_turn_acp_agent_loop.new_session(
cwd=str(Path.cwd()), mcp_servers=[]
)
session_id = session_response.session_id
fake_client: FakeClient = two_turn_acp_agent_loop.client # type: ignore
await two_turn_acp_agent_loop.prompt(
session_id=session_id, prompt=[TextContentBlock(type="text", text="hi")]
)
chunks_turn_1 = [
u
for u in fake_client._session_updates
if isinstance(u.update, AgentMessageChunk)
]
fake_client._session_updates.clear()
await two_turn_acp_agent_loop.prompt(
session_id=session_id,
prompt=[TextContentBlock(type="text", text="hi again")],
)
chunks_turn_2 = [
u
for u in fake_client._session_updates
if isinstance(u.update, AgentMessageChunk)
]
assert chunks_turn_1[0].update.message_id != chunks_turn_2[0].update.message_id