v2.14.0 (#743)
Co-authored-by: Alexis Tacnet <alexis@mistral.ai> Co-authored-by: Clément Drouin <clement.drouin@mistral.ai> Co-authored-by: Guillaume LE GOFF <guillaume.lgf@gmail.com> Co-authored-by: Lucas Marandat <31749711+lucasmrdt@users.noreply.github.com> Co-authored-by: Maxime Dolores <maxime.dolores@ext.mistral.ai> Co-authored-by: Pierre Rossinès <pierre.rossines@mistral.ai> Co-authored-by: Quentin <quentin.torroba@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: p.vezia <166131032+le-codeur-rapide@users.noreply.github.com> Co-authored-by: Hiba Chaabnia <Hiba-Chaabnia@users.noreply.github.com> Co-authored-by: Nikhil Bhima <nikhilbhima@users.noreply.github.com> Co-authored-by: Nkipohcs <Nkipohcs@users.noreply.github.com> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
parent
ad0d5c9520
commit
3f8487f761
197 changed files with 10819 additions and 2830 deletions
|
|
@ -1,243 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from acp import ReadTextFileResponse
|
||||
import pytest
|
||||
|
||||
from tests.mock.utils import collect_result
|
||||
from vibe.acp.tools.builtins.read_file import AcpReadFileState, ReadFile
|
||||
from vibe.core.tools.base import ToolError
|
||||
from vibe.core.tools.builtins.read_file import (
|
||||
ReadFileArgs,
|
||||
ReadFileResult,
|
||||
ReadFileToolConfig,
|
||||
)
|
||||
|
||||
|
||||
class MockClient:
|
||||
def __init__(
|
||||
self,
|
||||
file_content: str = "line 1\nline 2\nline 3",
|
||||
read_error: Exception | None = None,
|
||||
) -> None:
|
||||
self._file_content = file_content
|
||||
self._read_error = read_error
|
||||
self._read_text_file_called = False
|
||||
self._session_update_called = False
|
||||
self._last_read_params: dict[str, str | int | None] = {}
|
||||
|
||||
async def read_text_file(
|
||||
self,
|
||||
path: str,
|
||||
session_id: str,
|
||||
limit: int | None = None,
|
||||
line: int | None = None,
|
||||
**kwargs,
|
||||
) -> ReadTextFileResponse:
|
||||
self._read_text_file_called = True
|
||||
self._last_read_params = {
|
||||
"path": path,
|
||||
"session_id": session_id,
|
||||
"limit": limit,
|
||||
"line": line,
|
||||
}
|
||||
|
||||
if self._read_error:
|
||||
raise self._read_error
|
||||
|
||||
content = self._file_content
|
||||
if line is not None or limit is not None:
|
||||
lines = content.splitlines(keepends=True)
|
||||
start_line = (line or 1) - 1 # Convert to 0-indexed
|
||||
end_line = start_line + limit if limit is not None else len(lines)
|
||||
lines = lines[start_line:end_line]
|
||||
content = "".join(lines)
|
||||
|
||||
return ReadTextFileResponse(content=content)
|
||||
|
||||
async def session_update(self, session_id: str, update, **kwargs) -> None:
|
||||
self._session_update_called = True
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_client() -> MockClient:
|
||||
return MockClient()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def acp_read_file_tool(
|
||||
mock_client: MockClient, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> ReadFile:
|
||||
monkeypatch.chdir(tmp_path)
|
||||
config = ReadFileToolConfig()
|
||||
state = AcpReadFileState.model_construct(
|
||||
client=mock_client, # type: ignore[arg-type]
|
||||
session_id="test_session_123",
|
||||
)
|
||||
return ReadFile(config_getter=lambda: config, state=state)
|
||||
|
||||
|
||||
class TestAcpReadFileBasic:
|
||||
def test_get_name(self) -> None:
|
||||
assert ReadFile.get_name() == "read_file"
|
||||
|
||||
|
||||
class TestAcpReadFileExecution:
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_success(
|
||||
self, acp_read_file_tool: ReadFile, mock_client: MockClient, tmp_path: Path
|
||||
) -> None:
|
||||
test_file = tmp_path / "test_file.txt"
|
||||
test_file.touch()
|
||||
args = ReadFileArgs(path=str(test_file))
|
||||
result = await collect_result(acp_read_file_tool.run(args))
|
||||
|
||||
assert isinstance(result, ReadFileResult)
|
||||
assert result.path == str(test_file)
|
||||
assert result.content == "line 1\nline 2\nline 3"
|
||||
assert result.lines_read == 3
|
||||
assert mock_client._read_text_file_called
|
||||
|
||||
# Verify read_text_file was called correctly
|
||||
params = mock_client._last_read_params
|
||||
assert params["session_id"] == "test_session_123"
|
||||
assert params["path"] == str(test_file)
|
||||
assert params["line"] is None # offset=0 means no line specified
|
||||
assert params["limit"] is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_with_offset(
|
||||
self, mock_client: MockClient, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.chdir(tmp_path)
|
||||
test_file = tmp_path / "test_file.txt"
|
||||
test_file.touch()
|
||||
tool = ReadFile(
|
||||
config_getter=lambda: ReadFileToolConfig(),
|
||||
state=AcpReadFileState.model_construct(
|
||||
client=mock_client, session_id="test_session"
|
||||
),
|
||||
)
|
||||
|
||||
args = ReadFileArgs(path=str(test_file), offset=1)
|
||||
result = await collect_result(tool.run(args))
|
||||
|
||||
assert result.lines_read == 2
|
||||
assert result.content == "line 2\nline 3"
|
||||
|
||||
params = mock_client._last_read_params
|
||||
assert params["line"] == 2 # offset=1 means line 2 (1-indexed)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_with_limit(
|
||||
self, mock_client: MockClient, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.chdir(tmp_path)
|
||||
test_file = tmp_path / "test_file.txt"
|
||||
test_file.touch()
|
||||
tool = ReadFile(
|
||||
config_getter=lambda: ReadFileToolConfig(),
|
||||
state=AcpReadFileState.model_construct(
|
||||
client=mock_client, session_id="test_session"
|
||||
),
|
||||
)
|
||||
|
||||
args = ReadFileArgs(path=str(test_file), limit=2)
|
||||
result = await collect_result(tool.run(args))
|
||||
|
||||
assert result.lines_read == 2
|
||||
assert result.content == "line 1\nline 2\n"
|
||||
|
||||
params = mock_client._last_read_params
|
||||
assert params["limit"] == 2
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_with_offset_and_limit(
|
||||
self, mock_client: MockClient, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.chdir(tmp_path)
|
||||
test_file = tmp_path / "test_file.txt"
|
||||
test_file.touch()
|
||||
tool = ReadFile(
|
||||
config_getter=lambda: ReadFileToolConfig(),
|
||||
state=AcpReadFileState.model_construct(
|
||||
client=mock_client, session_id="test_session"
|
||||
),
|
||||
)
|
||||
|
||||
args = ReadFileArgs(path=str(test_file), offset=1, limit=1)
|
||||
result = await collect_result(tool.run(args))
|
||||
|
||||
assert result.lines_read == 1
|
||||
assert result.content == "line 2\n"
|
||||
|
||||
params = mock_client._last_read_params
|
||||
assert params["line"] == 2
|
||||
assert params["limit"] == 1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_read_error(
|
||||
self, mock_client: MockClient, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.chdir(tmp_path)
|
||||
mock_client._read_error = RuntimeError("File not found")
|
||||
test_file = tmp_path / "test.txt"
|
||||
test_file.touch()
|
||||
tool = ReadFile(
|
||||
config_getter=lambda: ReadFileToolConfig(),
|
||||
state=AcpReadFileState.model_construct(
|
||||
client=mock_client, session_id="test_session"
|
||||
),
|
||||
)
|
||||
|
||||
args = ReadFileArgs(path=str(test_file))
|
||||
with pytest.raises(ToolError) as exc_info:
|
||||
await collect_result(tool.run(args))
|
||||
|
||||
assert str(exc_info.value) == f"Error reading {test_file}: File not found"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_without_connection(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.chdir(tmp_path)
|
||||
test_file = tmp_path / "test.txt"
|
||||
test_file.touch()
|
||||
tool = ReadFile(
|
||||
config_getter=lambda: ReadFileToolConfig(),
|
||||
state=AcpReadFileState.model_construct(
|
||||
client=None, session_id="test_session"
|
||||
),
|
||||
)
|
||||
|
||||
args = ReadFileArgs(path=str(test_file))
|
||||
with pytest.raises(ToolError) as exc_info:
|
||||
await collect_result(tool.run(args))
|
||||
|
||||
assert (
|
||||
str(exc_info.value)
|
||||
== "Client not available in tool state. This tool can only be used within an ACP session."
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_without_session_id(
|
||||
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.chdir(tmp_path)
|
||||
test_file = tmp_path / "test.txt"
|
||||
test_file.touch()
|
||||
mock_client = MockClient()
|
||||
tool = ReadFile(
|
||||
config_getter=lambda: ReadFileToolConfig(),
|
||||
state=AcpReadFileState.model_construct(client=mock_client, session_id=None),
|
||||
)
|
||||
|
||||
args = ReadFileArgs(path=str(test_file))
|
||||
with pytest.raises(ToolError) as exc_info:
|
||||
await collect_result(tool.run(args))
|
||||
|
||||
assert (
|
||||
str(exc_info.value)
|
||||
== "Session ID not available in tool state. This tool can only be used within an ACP session."
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue