diff --git a/core/plugin/builtin_plugins/kira-ai/tags.py b/core/plugin/builtin_plugins/kira-ai/tags.py index 0a0ac217..f6a70b3a 100644 --- a/core/plugin/builtin_plugins/kira-ai/tags.py +++ b/core/plugin/builtin_plugins/kira-ai/tags.py @@ -81,9 +81,8 @@ async def handle(self, value: str, **kwargs) -> list[BaseMessageElement]: if not ref_file.is_file(): message_logger.warning(f"Image reference not found: {ref_file}, skipped") continue - img_extension = ref_file.suffix.lstrip(".") bs64 = await image_to_base64(str(ref_file)) - ref_images.append(Image(image=bs64, name=p, mime=f"image/{img_extension}")) + ref_images.append(Image(image=bs64, name=p)) try: image_client = self.ctx.provider_mgr.get_default_image() except ValueError as exc: @@ -207,13 +206,12 @@ async def handle(self, value: str, **kwargs) -> list[BaseMessageElement]: else: ref_file = get_data_path() / ref_img_path if ref_file.is_file(): - img_extension = ref_file.suffix.lstrip(".") bs64 = await image_to_base64(str(ref_file)) image_client = self.ctx.provider_mgr.get_default_image() if not image_client: message_logger.error("Failed to get image client, please set default image model in Configuration") return [] - img_res = await image_to_image(image_client, value, image=Image(image=bs64, name=ref_img_path, mime=f"image/{img_extension}")) + img_res = await image_to_image(image_client, value, image=Image(image=bs64, name=ref_img_path)) if img_res: return [img_res] message_logger.warning("Invalid selfie image result") diff --git a/core/provider/src/openai/model_clients.py b/core/provider/src/openai/model_clients.py index 4cc4ea67..7ca1f813 100644 --- a/core/provider/src/openai/model_clients.py +++ b/core/provider/src/openai/model_clients.py @@ -96,6 +96,8 @@ def _image_file_from_data_url( mime_type, *parameters = header.removeprefix("data:").split(";") mime_type = mime_type.lower() + if mime_type == "image/jpg": + mime_type = "image/jpeg" if "base64" not in {parameter.lower() for parameter in parameters}: raise ValueError("Image Data URL must contain Base64 data") diff --git a/tests/test_openai_image_client.py b/tests/test_openai_image_client.py index b41676f5..a86936c1 100644 --- a/tests/test_openai_image_client.py +++ b/tests/test_openai_image_client.py @@ -167,3 +167,30 @@ async def test_image_edit_requires_input_image(): await client.image_to_image("edit it", []) edit.assert_not_awaited() + + +@pytest.mark.asyncio +@pytest.mark.parametrize("mime", ["image/jpg", "image/JPG", "image/jpeg"]) +@pytest.mark.parametrize("source", ["data_url", "base64"]) +async def test_image_edit_normalizes_jpeg_mime(mime, source): + jpeg_bytes = b"\xff\xd8\xff\xe0jpeg" + encoded = base64.b64encode(jpeg_bytes).decode() + image = ( + Image(image=f"data:{mime};base64,{encoded}") + if source == "data_url" + else Image(image=encoded, mime=mime) + ) + edit = AsyncMock( + return_value=SimpleNamespace( + data=[SimpleNamespace(url=None, b64_json=PNG_BASE64)] + ) + ) + client = _configured_client(SimpleNamespace(edit=edit)) + + result = await client.image_to_image("edit it", image) + + assert result.image == PNG_BASE64 + edit.assert_awaited_once() + assert edit.await_args.kwargs["image"] == ( + "image_0.jpg", jpeg_bytes, "image/jpeg" + )