v2.3.0 (#429)
Co-authored-by: Carlo <carloantonio.patti@mistral.ai> Co-authored-by: Mathias Gesbert <mathias.gesbert@mistral.ai> Co-authored-by: Clement Sirieix <clem.sirieix@gmail.com> Co-authored-by: Michel Thomazo <michel.thomazo@mistral.ai> Co-authored-by: Clément Drouin <clement.drouin@mistral.ai> Co-authored-by: Vincent Guilloux <vincent.guilloux@mistral.ai> Co-authored-by: Thomas Kenbeek <thomas.kenbeek@mistral.ai> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
parent
a560a47ce8
commit
5d2e01a6d7
139 changed files with 7152 additions and 1457 deletions
|
|
@ -1,9 +1,10 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from collections import Counter
|
||||
from collections.abc import Callable
|
||||
from http import HTTPStatus
|
||||
from typing import cast
|
||||
from unittest.mock import AsyncMock
|
||||
from unittest.mock import AsyncMock, Mock
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
|
@ -180,6 +181,27 @@ async def test_act_streams_chunks_in_order() -> None:
|
|||
assert agent.messages[-1].content == "Hello from Vibe! More and end"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_act_streaming_does_not_cleanup_tmp_files_directly() -> None:
|
||||
backend = FakeBackend([
|
||||
mock_llm_chunk(content="Hello"),
|
||||
mock_llm_chunk(content=" from"),
|
||||
mock_llm_chunk(content=" Vibe"),
|
||||
])
|
||||
agent = build_test_agent_loop(
|
||||
config=make_config(), backend=backend, enable_streaming=True
|
||||
)
|
||||
agent.session_logger.save_interaction = AsyncMock(return_value=None)
|
||||
cleanup_spy = Mock()
|
||||
agent.session_logger.maybe_cleanup_tmp_files = cleanup_spy
|
||||
|
||||
events = [event async for event in agent.act("Stream, please.")]
|
||||
|
||||
assistant_events = [event for event in events if isinstance(event, AssistantEvent)]
|
||||
assert len(assistant_events) == 3
|
||||
assert cleanup_spy.call_count == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_act_handles_streaming_with_tool_call_events_in_sequence() -> None:
|
||||
todo_tool_call = ToolCall(
|
||||
|
|
@ -210,6 +232,7 @@ async def test_act_handles_streaming_with_tool_call_events_in_sequence() -> None
|
|||
UserMessageEvent,
|
||||
AssistantEvent,
|
||||
ToolCallEvent,
|
||||
ToolCallEvent,
|
||||
ToolResultEvent,
|
||||
AssistantEvent,
|
||||
]
|
||||
|
|
@ -217,12 +240,17 @@ async def test_act_handles_streaming_with_tool_call_events_in_sequence() -> None
|
|||
assert isinstance(events[1], AssistantEvent)
|
||||
assert events[1].content == "Checking your todos."
|
||||
assert isinstance(events[2], ToolCallEvent)
|
||||
assert events[2].args is None # streaming event
|
||||
assert events[2].tool_call_id == "tc_stream"
|
||||
assert events[2].tool_name == "todo"
|
||||
assert isinstance(events[3], ToolResultEvent)
|
||||
assert events[3].error is None
|
||||
assert events[3].skipped is False
|
||||
assert isinstance(events[4], AssistantEvent)
|
||||
assert events[4].content == "Done reviewing todos."
|
||||
assert isinstance(events[3], ToolCallEvent)
|
||||
assert events[3].args is not None
|
||||
assert events[3].tool_name == "todo"
|
||||
assert isinstance(events[4], ToolResultEvent)
|
||||
assert events[4].error is None
|
||||
assert events[4].skipped is False
|
||||
assert isinstance(events[5], AssistantEvent)
|
||||
assert events[5].content == "Done reviewing todos."
|
||||
assert agent.messages[-1].content == "Done reviewing todos."
|
||||
|
||||
|
||||
|
|
@ -250,21 +278,27 @@ async def test_act_handles_tool_call_chunk_with_content() -> None:
|
|||
|
||||
events = [event async for event in agent.act("Check todos with content.")]
|
||||
|
||||
assert [type(event) for event in events] == [
|
||||
UserMessageEvent,
|
||||
AssistantEvent,
|
||||
AssistantEvent,
|
||||
AssistantEvent,
|
||||
ToolCallEvent,
|
||||
ToolResultEvent,
|
||||
]
|
||||
assert isinstance(events[0], UserMessageEvent)
|
||||
assert isinstance(events[1], AssistantEvent)
|
||||
assert isinstance(events[2], AssistantEvent)
|
||||
assert isinstance(events[3], AssistantEvent)
|
||||
assert events[1].content == "Preparing "
|
||||
assert events[2].content == "todo request"
|
||||
assert events[3].content == " complete"
|
||||
event_types = [type(e) for e in events]
|
||||
assert Counter(event_types) == Counter({
|
||||
UserMessageEvent: 1,
|
||||
AssistantEvent: 3,
|
||||
ToolCallEvent: 2,
|
||||
ToolResultEvent: 1,
|
||||
})
|
||||
|
||||
tool_call_events = [e for e in events if isinstance(e, ToolCallEvent)]
|
||||
assert len(tool_call_events) == 2
|
||||
assert any(
|
||||
tc.args is None and tc.tool_call_id == "tc_content" for tc in tool_call_events
|
||||
)
|
||||
assert any(tc.args is not None for tc in tool_call_events)
|
||||
|
||||
assistant_events = [e for e in events if isinstance(e, AssistantEvent)]
|
||||
assistant_contents = {e.content for e in assistant_events}
|
||||
assert "Preparing " in assistant_contents
|
||||
assert "todo request" in assistant_contents
|
||||
assert " complete" in assistant_contents
|
||||
|
||||
assert any(
|
||||
m.role == Role.assistant and m.content == "Preparing todo request complete"
|
||||
for m in agent.messages
|
||||
|
|
@ -304,17 +338,21 @@ async def test_act_merges_streamed_tool_call_arguments() -> None:
|
|||
UserMessageEvent,
|
||||
AssistantEvent,
|
||||
ToolCallEvent,
|
||||
ToolCallEvent,
|
||||
ToolResultEvent,
|
||||
]
|
||||
assert isinstance(events[0], UserMessageEvent)
|
||||
call_event = events[2]
|
||||
assert isinstance(events[2], ToolCallEvent)
|
||||
assert events[2].args is None # streaming event
|
||||
assert events[2].tool_call_id == "tc_merge"
|
||||
call_event = events[3]
|
||||
assert isinstance(call_event, ToolCallEvent)
|
||||
assert call_event.tool_call_id == "tc_merge"
|
||||
call_args = cast(TodoArgs, call_event.args)
|
||||
assert call_args.action == "read"
|
||||
assert isinstance(events[3], ToolResultEvent)
|
||||
assert events[3].error is None
|
||||
assert events[3].skipped is False
|
||||
assert isinstance(events[4], ToolResultEvent)
|
||||
assert events[4].error is None
|
||||
assert events[4].skipped is False
|
||||
assistant_with_calls = next(
|
||||
m for m in agent.messages if m.role == Role.assistant and m.tool_calls
|
||||
)
|
||||
|
|
@ -371,6 +409,7 @@ async def test_act_handles_user_cancellation_during_streaming() -> None:
|
|||
assert [type(event) for event in events] == [
|
||||
UserMessageEvent,
|
||||
AssistantEvent,
|
||||
ToolCallEvent,
|
||||
AssistantEvent,
|
||||
ToolCallEvent,
|
||||
ToolResultEvent,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue