v2.9.0 (#641)
Co-authored-by: Antoine <33425718+anth2o@users.noreply.github.com> Co-authored-by: Bastien <bastien.baret@gmail.com> Co-authored-by: Clément Sirieix <clement.sirieix@mistral.ai> Co-authored-by: Kim-Adeline Miguel <51720070+kimadeline@users.noreply.github.com> Co-authored-by: Mathias Gesbert <mathias.gesbert@mistral.ai> Co-authored-by: Maxime Dolores <maxime.dolores@ext.mistral.ai> Co-authored-by: Michel Thomazo <51709227+michelTho@users.noreply.github.com> Co-authored-by: Nelson PROIA <144663685+Nelson-PROIA@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: Robin Gullo <robin.gullo@mistral.ai> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
parent
a83c81ecf5
commit
632ea8c032
253 changed files with 13965 additions and 2525 deletions
|
|
@ -39,31 +39,31 @@ class TestShortSessionId:
|
|||
|
||||
class TestListRemoteResumeSessions:
|
||||
@pytest.mark.asyncio
|
||||
async def test_returns_empty_when_nuage_disabled(self) -> None:
|
||||
async def test_returns_empty_when_vibe_code_disabled(self) -> None:
|
||||
config = MagicMock()
|
||||
config.nuage_enabled = False
|
||||
config.nuage_api_key = "key"
|
||||
config.vibe_code_enabled = False
|
||||
config.vibe_code_api_key = "key"
|
||||
result = await list_remote_resume_sessions(config)
|
||||
assert result == []
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_returns_empty_when_no_api_key(self) -> None:
|
||||
config = MagicMock()
|
||||
config.nuage_enabled = True
|
||||
config.nuage_api_key = None
|
||||
config.vibe_code_enabled = True
|
||||
config.vibe_code_api_key = None
|
||||
result = await list_remote_resume_sessions(config)
|
||||
assert result == []
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_returns_empty_when_both_missing(self) -> None:
|
||||
config = MagicMock()
|
||||
config.nuage_enabled = False
|
||||
config.nuage_api_key = None
|
||||
config.vibe_code_enabled = False
|
||||
config.vibe_code_api_key = None
|
||||
result = await list_remote_resume_sessions(config)
|
||||
assert result == []
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_filters_only_active_statuses(self) -> None:
|
||||
async def test_passes_active_statuses_to_api(self) -> None:
|
||||
from datetime import datetime
|
||||
|
||||
from vibe.core.nuage.workflow import (
|
||||
|
|
@ -79,13 +79,6 @@ class TestListRemoteResumeSessions:
|
|||
start_time=datetime(2026, 1, 1),
|
||||
end_time=None,
|
||||
)
|
||||
completed = WorkflowExecutionWithoutResultResponse(
|
||||
workflow_name="vibe",
|
||||
execution_id="exec-completed",
|
||||
status=WorkflowExecutionStatus.COMPLETED,
|
||||
start_time=datetime(2026, 1, 1),
|
||||
end_time=datetime(2026, 1, 2),
|
||||
)
|
||||
retrying = WorkflowExecutionWithoutResultResponse(
|
||||
workflow_name="vibe",
|
||||
execution_id="exec-retrying",
|
||||
|
|
@ -93,17 +86,90 @@ class TestListRemoteResumeSessions:
|
|||
start_time=datetime(2026, 1, 1),
|
||||
end_time=None,
|
||||
)
|
||||
continued = WorkflowExecutionWithoutResultResponse(
|
||||
workflow_name="vibe",
|
||||
execution_id="exec-continued",
|
||||
status=WorkflowExecutionStatus.CONTINUED_AS_NEW,
|
||||
start_time=datetime(2026, 1, 1),
|
||||
end_time=None,
|
||||
)
|
||||
|
||||
mock_response = WorkflowExecutionListResponse(
|
||||
executions=[running, completed, retrying]
|
||||
executions=[running, retrying, continued]
|
||||
)
|
||||
|
||||
config = MagicMock()
|
||||
config.nuage_enabled = True
|
||||
config.nuage_api_key = "test-key"
|
||||
config.nuage_base_url = "https://test.example.com"
|
||||
config.vibe_code_enabled = True
|
||||
config.vibe_code_api_key = "test-key"
|
||||
config.vibe_code_base_url = "https://test.example.com"
|
||||
config.api_timeout = 30
|
||||
config.nuage_workflow_id = "workflow-1"
|
||||
config.vibe_code_workflow_id = "workflow-1"
|
||||
|
||||
with patch("vibe.core.session.resume_sessions.WorkflowsClient") as MockClient:
|
||||
mock_client = AsyncMock()
|
||||
mock_client.get_workflow_runs.return_value = mock_response
|
||||
MockClient.return_value.__aenter__ = AsyncMock(return_value=mock_client)
|
||||
MockClient.return_value.__aexit__ = AsyncMock(return_value=False)
|
||||
|
||||
result = await list_remote_resume_sessions(config)
|
||||
|
||||
assert len(result) == 3
|
||||
session_ids = {s.session_id for s in result}
|
||||
assert "exec-running" in session_ids
|
||||
assert "exec-retrying" in session_ids
|
||||
assert "exec-continued" in session_ids
|
||||
assert all(s.source == "remote" for s in result)
|
||||
|
||||
mock_client.get_workflow_runs.assert_called_once_with(
|
||||
workflow_identifier="workflow-1",
|
||||
page_size=50,
|
||||
status=[
|
||||
WorkflowExecutionStatus.RUNNING,
|
||||
WorkflowExecutionStatus.RETRYING_AFTER_ERROR,
|
||||
WorkflowExecutionStatus.CONTINUED_AS_NEW,
|
||||
],
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_deduplicates_execution_ids_keeps_latest(self) -> None:
|
||||
from datetime import datetime
|
||||
|
||||
from vibe.core.nuage.workflow import (
|
||||
WorkflowExecutionListResponse,
|
||||
WorkflowExecutionStatus,
|
||||
WorkflowExecutionWithoutResultResponse,
|
||||
)
|
||||
|
||||
older = WorkflowExecutionWithoutResultResponse(
|
||||
workflow_name="vibe",
|
||||
execution_id="exec-1",
|
||||
status=WorkflowExecutionStatus.RUNNING,
|
||||
start_time=datetime(2026, 1, 1),
|
||||
end_time=None,
|
||||
)
|
||||
newer = WorkflowExecutionWithoutResultResponse(
|
||||
workflow_name="vibe",
|
||||
execution_id="exec-1",
|
||||
status=WorkflowExecutionStatus.RUNNING,
|
||||
start_time=datetime(2026, 1, 5),
|
||||
end_time=None,
|
||||
)
|
||||
other = WorkflowExecutionWithoutResultResponse(
|
||||
workflow_name="vibe",
|
||||
execution_id="exec-2",
|
||||
status=WorkflowExecutionStatus.RUNNING,
|
||||
start_time=datetime(2026, 1, 3),
|
||||
end_time=None,
|
||||
)
|
||||
|
||||
mock_response = WorkflowExecutionListResponse(executions=[older, newer, other])
|
||||
|
||||
config = MagicMock()
|
||||
config.vibe_code_enabled = True
|
||||
config.vibe_code_api_key = "test-key"
|
||||
config.vibe_code_base_url = "https://test.example.com"
|
||||
config.api_timeout = 30
|
||||
config.vibe_code_workflow_id = "workflow-1"
|
||||
|
||||
with patch("vibe.core.session.resume_sessions.WorkflowsClient") as MockClient:
|
||||
mock_client = AsyncMock()
|
||||
|
|
@ -114,8 +180,54 @@ class TestListRemoteResumeSessions:
|
|||
result = await list_remote_resume_sessions(config)
|
||||
|
||||
assert len(result) == 2
|
||||
session_ids = {s.session_id for s in result}
|
||||
assert "exec-running" in session_ids
|
||||
assert "exec-retrying" in session_ids
|
||||
assert "exec-completed" not in session_ids
|
||||
assert all(s.source == "remote" for s in result)
|
||||
by_id = {s.session_id: s for s in result}
|
||||
assert by_id["exec-1"].end_time == datetime(2026, 1, 5).isoformat()
|
||||
assert "exec-2" in by_id
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_dedup_keeps_latest_start_time_when_previous_has_end_time(
|
||||
self,
|
||||
) -> None:
|
||||
from datetime import datetime
|
||||
|
||||
from vibe.core.nuage.workflow import (
|
||||
WorkflowExecutionListResponse,
|
||||
WorkflowExecutionStatus,
|
||||
WorkflowExecutionWithoutResultResponse,
|
||||
)
|
||||
|
||||
previous = WorkflowExecutionWithoutResultResponse(
|
||||
workflow_name="vibe",
|
||||
execution_id="exec-1",
|
||||
status=WorkflowExecutionStatus.RETRYING_AFTER_ERROR,
|
||||
start_time=datetime(2026, 1, 1),
|
||||
end_time=datetime(2026, 1, 10),
|
||||
)
|
||||
newer = WorkflowExecutionWithoutResultResponse(
|
||||
workflow_name="vibe",
|
||||
execution_id="exec-1",
|
||||
status=WorkflowExecutionStatus.RUNNING,
|
||||
start_time=datetime(2026, 1, 5),
|
||||
end_time=None,
|
||||
)
|
||||
|
||||
mock_response = WorkflowExecutionListResponse(executions=[previous, newer])
|
||||
|
||||
config = MagicMock()
|
||||
config.vibe_code_enabled = True
|
||||
config.vibe_code_api_key = "test-key"
|
||||
config.vibe_code_base_url = "https://test.example.com"
|
||||
config.api_timeout = 30
|
||||
config.vibe_code_workflow_id = "workflow-1"
|
||||
|
||||
with patch("vibe.core.session.resume_sessions.WorkflowsClient") as MockClient:
|
||||
mock_client = AsyncMock()
|
||||
mock_client.get_workflow_runs.return_value = mock_response
|
||||
MockClient.return_value.__aenter__ = AsyncMock(return_value=mock_client)
|
||||
MockClient.return_value.__aexit__ = AsyncMock(return_value=False)
|
||||
|
||||
result = await list_remote_resume_sessions(config)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].session_id == "exec-1"
|
||||
assert result[0].status == WorkflowExecutionStatus.RUNNING
|
||||
|
|
|
|||
|
|
@ -39,10 +39,11 @@ def create_test_session():
|
|||
messages: list[LLMMessage] | None = None,
|
||||
metadata: dict | None = None,
|
||||
encoding: str = "utf-8",
|
||||
working_directory: Path | None = Path("/test"),
|
||||
) -> Path:
|
||||
"""Create a test session directory with messages and metadata files."""
|
||||
# Create session directory
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
|
||||
session_folder = session_dir / f"test_{timestamp}_{session_id[:8]}"
|
||||
session_folder.mkdir(exist_ok=True)
|
||||
|
||||
|
|
@ -79,7 +80,7 @@ def create_test_session():
|
|||
},
|
||||
"system_prompt": {"content": "System prompt", "role": "system"},
|
||||
"username": "testuser",
|
||||
"environment": {"working_directory": "/test"},
|
||||
"environment": {"working_directory": str(working_directory)},
|
||||
"git_commit": None,
|
||||
"git_branch": None,
|
||||
}
|
||||
|
|
@ -129,6 +130,93 @@ class TestSessionLoaderFindLatestSession:
|
|||
assert result.exists()
|
||||
assert result == latest
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("cwd", "expected_id"),
|
||||
[
|
||||
pytest.param(
|
||||
Path("/home/user/project-a"), "aaaaaaaa", id="get_latest_in_existing_a"
|
||||
),
|
||||
pytest.param(
|
||||
Path("/home/user/project-b"), "bbbbbbbb", id="get_latest_in_existing_b"
|
||||
),
|
||||
pytest.param(
|
||||
Path("/home/user/project-c"), None, id="get_latest_in_missing_c"
|
||||
),
|
||||
pytest.param(None, "aaaaaaaa", id="get_latest_globally"),
|
||||
],
|
||||
)
|
||||
def test_find_latest_session_cwd_filtering(
|
||||
self,
|
||||
session_config: SessionLoggingConfig,
|
||||
create_test_session,
|
||||
cwd: Path | None,
|
||||
expected_id: str | None,
|
||||
) -> None:
|
||||
session_dir = Path(session_config.save_dir)
|
||||
|
||||
create_test_session(
|
||||
session_dir,
|
||||
"aaaaaaaa-session",
|
||||
working_directory=Path("/home/user/project-a"),
|
||||
)
|
||||
time.sleep(0.01)
|
||||
create_test_session(
|
||||
session_dir,
|
||||
"bbbbbbbb-session",
|
||||
working_directory=Path("/home/user/project-b"),
|
||||
)
|
||||
time.sleep(0.01)
|
||||
second_b = create_test_session(
|
||||
session_dir,
|
||||
"bbbbbbbb-session",
|
||||
working_directory=Path("/home/user/project-b"),
|
||||
)
|
||||
time.sleep(0.01)
|
||||
second_a = create_test_session(
|
||||
session_dir,
|
||||
"aaaaaaaa-session",
|
||||
working_directory=Path("/home/user/project-a"),
|
||||
)
|
||||
|
||||
assert len(list(session_dir.glob("test_*"))) == 4
|
||||
|
||||
expected: Path | None
|
||||
if expected_id == "aaaaaaaa":
|
||||
expected = second_a
|
||||
elif expected_id == "bbbbbbbb":
|
||||
expected = second_b
|
||||
elif expected_id is None:
|
||||
expected = None
|
||||
else:
|
||||
raise NotImplementedError(expected_id)
|
||||
|
||||
result = SessionLoader.find_latest_session(
|
||||
session_config, working_directory=cwd
|
||||
)
|
||||
assert result == expected
|
||||
|
||||
def test_find_latest_session_cwd_filtering_skips_invalid_metadata(
|
||||
self, session_config: SessionLoggingConfig, create_test_session
|
||||
) -> None:
|
||||
session_dir = Path(session_config.save_dir)
|
||||
expected = create_test_session(
|
||||
session_dir,
|
||||
"valid-cwd-session",
|
||||
working_directory=Path("/home/user/project-a"),
|
||||
)
|
||||
time.sleep(0.01)
|
||||
invalid_metadata_session = create_test_session(
|
||||
session_dir,
|
||||
"invalid-metadata",
|
||||
working_directory=Path("/home/user/project-a"),
|
||||
)
|
||||
(invalid_metadata_session / "meta.json").write_text("{}")
|
||||
|
||||
result = SessionLoader.find_latest_session(
|
||||
session_config, working_directory=Path("/home/user/project-a")
|
||||
)
|
||||
assert result == expected
|
||||
|
||||
def test_find_latest_session_nonexistent_save_dir(self) -> None:
|
||||
"""Test finding latest session when save directory doesn't exist."""
|
||||
# Modify config to point to non-existent directory
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue