Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 16 additions & 13 deletions pyrit/backend/services/converter_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,20 +196,23 @@ async def preview_conversion_async(self, *, request: ConverterPreviewRequest) ->
)
await serializer.save_b64_image_async(data=value)
original_value = str(serializer.value)
# Already an existing file on disk — keep as-is
elif Path(original_value).is_file():
pass
else:
# Treat as raw base64
ext = DEFAULT_MEDIA_EXTENSIONS.get(str(data_type), ".bin")

serializer = data_serializer_factory(
category="prompt-memory-entries",
data_type=data_type,
extension=ext,
)
await serializer.save_b64_image_async(data=original_value)
original_value = str(serializer.value)
try:
is_existing_file = Path(original_value).is_file()
except (OSError, ValueError):
is_existing_file = False
Comment on lines +202 to +203

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

couldn't there be a value error or os error that is not related to the file existing ?


if not is_existing_file:
# Treat as raw base64
ext = DEFAULT_MEDIA_EXTENSIONS.get(str(data_type), ".bin")

serializer = data_serializer_factory(
category="prompt-memory-entries",
data_type=data_type,
extension=ext,
)
await serializer.save_b64_image_async(data=original_value)
original_value = str(serializer.value)

converters = self._gather_converters(converter_ids=request.converter_ids)
steps, final_value, final_type = await self._apply_converters_async(
Expand Down
68 changes: 66 additions & 2 deletions tests/unit/backend/test_converter_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ async def test_preview_conversion_persists_raw_base64_for_audio_path(self) -> No
mock_serializer.value = "/tmp/persisted.wav"
mock_serializer.save_b64_image_async = AsyncMock()

raw_b64 = "UklGRiQAAABXQVZF"
raw_b64 = "UklGRiQ" + "A" * 5000
request = ConverterPreviewRequest(
original_value=raw_b64,
original_value_data_type="audio_path",
Expand All @@ -452,13 +452,77 @@ async def test_preview_conversion_persists_raw_base64_for_audio_path(self) -> No
"pyrit.backend.services.converter_service.data_serializer_factory",
return_value=mock_serializer,
) as mock_factory:
await service.preview_conversion_async(request=request)
result = await service.preview_conversion_async(request=request)

mock_factory.assert_called_once()
# ext is the audio_path mapping from DEFAULT_MEDIA_EXTENSIONS
assert mock_factory.call_args.kwargs["extension"] == ".wav"
assert mock_factory.call_args.kwargs["data_type"] == "audio_path"
mock_serializer.save_b64_image_async.assert_awaited_once_with(data=raw_b64)
assert result.model_dump(mode="json")["original_value"] == raw_b64

async def test_preview_conversion_persists_raw_base64_when_path_inspection_fails(self) -> None:
"""Filesystem inspection failures classify the value as raw base64."""
service = ConverterService()
mock_serializer = MagicMock()
mock_serializer.value = "/tmp/persisted.wav"
mock_serializer.save_b64_image_async = AsyncMock()
raw_b64 = "UklGRiQAAABXQVZF"
request = ConverterPreviewRequest(
original_value=raw_b64,
original_value_data_type="audio_path",
converter_ids=[],
)

with (
patch.object(Path, "is_file", side_effect=OSError("path inspection failed")),
patch(
"pyrit.backend.services.converter_service.data_serializer_factory",
return_value=mock_serializer,
),
):
result = await service.preview_conversion_async(request=request)

mock_serializer.save_b64_image_async.assert_awaited_once_with(data=raw_b64)
assert result.converted_value == "/tmp/persisted.wav"

async def test_preview_conversion_propagates_invalid_base64_error_after_path_failure(self) -> None:
"""Errors from raw base64 persistence are not mistaken for path inspection failures."""
service = ConverterService()
mock_serializer = MagicMock()
mock_serializer.save_b64_image_async = AsyncMock(side_effect=ValueError("invalid base64"))
request = ConverterPreviewRequest(
original_value="A",
original_value_data_type="audio_path",
converter_ids=[],
)

with (
patch.object(Path, "is_file", side_effect=ValueError("invalid path")),
patch(
"pyrit.backend.services.converter_service.data_serializer_factory",
return_value=mock_serializer,
),
pytest.raises(ValueError, match="invalid base64"),
):
await service.preview_conversion_async(request=request)

async def test_preview_conversion_preserves_existing_file(self, tmp_path: Path) -> None:
"""Existing local media paths pass through without being persisted again."""
service = ConverterService()
media_path = tmp_path / "input.wav"
media_path.write_bytes(b"RIFF")
request = ConverterPreviewRequest(
original_value=str(media_path),
original_value_data_type="audio_path",
converter_ids=[],
)

with patch("pyrit.backend.services.converter_service.data_serializer_factory") as mock_factory:
result = await service.preview_conversion_async(request=request)

mock_factory.assert_not_called()
assert result.converted_value == str(media_path)


class TestGetConverterObjectsForIds:
Expand Down
Loading